omk-protocol 0.98.1 → 0.98.3
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/CHANGELOG.md +9 -0
- package/README.md +7 -0
- package/dist/claims/claim-blocking-cut.d.ts +17 -0
- package/dist/claims/claim-blocking-cut.d.ts.map +1 -0
- package/dist/claims/claim-blocking-cut.js +68 -0
- package/dist/claims/claim-blocking-cut.js.map +1 -0
- package/dist/claims/claim-closure.d.ts +30 -0
- package/dist/claims/claim-closure.d.ts.map +1 -0
- package/dist/claims/claim-closure.js +207 -0
- package/dist/claims/claim-closure.js.map +1 -0
- package/dist/claims/claim-graph.d.ts +30 -0
- package/dist/claims/claim-graph.d.ts.map +1 -0
- package/dist/claims/claim-graph.js +149 -0
- package/dist/claims/claim-graph.js.map +1 -0
- package/dist/claims/claim-types.d.ts +102 -0
- package/dist/claims/claim-types.d.ts.map +1 -0
- package/dist/claims/claim-types.js +34 -0
- package/dist/claims/claim-types.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/package.json +3 -2
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## [0.98.3] - 2026-09-06
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
|
|
7
|
+
- Claim Closure Graph v1 types, validation, pure proof-closure evaluation and minimal blocking-cut analysis. Required/advisory claims, source/environment bindings, stale observations, trust floors, witness groups, workspace scope and open effects remain distinct. Callers must supply trustworthy evidence; a result is not a runner attestation or release approval.
|
|
8
|
+
|
|
9
|
+
Earlier lockstep releases predate this package-specific changelog; see the repository release notes.
|
package/README.md
CHANGED
|
@@ -13,6 +13,13 @@ TaskSpec -> ExecutionAttempt -> Observation -> EvaluationResult -> RuntimeDecisi
|
|
|
13
13
|
- `ClaimEvaluation`, `EvaluationResult`, `RuntimeDecision`, `WaiverRecord`
|
|
14
14
|
- Runtime parsers for every top-level record
|
|
15
15
|
- `evaluateTask()` and `reduceRuntimeDecision()`
|
|
16
|
+
- Claim Closure Graph v1: `evaluateProofClosure()`, `validateClaimGraph()`,
|
|
17
|
+
`minimalBlockingCut()` and the readonly claim/observation/waiver vocabulary
|
|
18
|
+
|
|
19
|
+
The claim-closure API is explicit. It checks supplied source/environment bindings,
|
|
20
|
+
trust floors, witness groups, expiry, workspace completeness and unresolved-effect
|
|
21
|
+
boundaries; those supplied facts remain caller trust boundaries. It does not attest a
|
|
22
|
+
runner or automatically decide an ordinary chat turn.
|
|
16
23
|
|
|
17
24
|
The package does not execute tools, persist records, schedule work, choose providers, or infer topology. Retry and failover counts are derived from attempt records rather than stored counters.
|
|
18
25
|
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal blocking cut: the smallest set of leaf claims whose closure would
|
|
3
|
+
* unblock every blocking root, so a user sees "fix these two" instead of a
|
|
4
|
+
* dump of every intermediate claim on the path.
|
|
5
|
+
*
|
|
6
|
+
* The graph is an all/any DAG, so the cut is computed by one memoized pass:
|
|
7
|
+
* an `all` node needs every blocking required child's cut, an `any` node
|
|
8
|
+
* needs only the cheapest child's cut (ties broken by claim id, so the
|
|
9
|
+
* explanation is stable across runs), and a node that is blocked without any
|
|
10
|
+
* blocking required child — a composite with its own counterexample — is its
|
|
11
|
+
* own cut. Advisory children never enter a cut: they cannot block a parent.
|
|
12
|
+
* Bounded by O(V + E); no general minimum-hitting-set search.
|
|
13
|
+
*/
|
|
14
|
+
import type { ClaimClosureEvaluation, ClaimNode, ClaimVerdict } from "./claim-types.ts";
|
|
15
|
+
export declare function isBlockingVerdict(verdict: ClaimVerdict): boolean;
|
|
16
|
+
export declare function minimalBlockingCut(claims: ReadonlyMap<string, ClaimNode>, evaluations: ReadonlyMap<string, ClaimClosureEvaluation>, rootIds: readonly string[]): readonly string[];
|
|
17
|
+
//# sourceMappingURL=claim-blocking-cut.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claim-blocking-cut.d.ts","sourceRoot":"","sources":["../../src/claims/claim-blocking-cut.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,sBAAsB,EAAE,SAAS,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAUxF,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAEhE;AAoBD,wBAAgB,kBAAkB,CACjC,MAAM,EAAE,WAAW,CAAC,MAAM,EAAE,SAAS,CAAC,EACtC,WAAW,EAAE,WAAW,CAAC,MAAM,EAAE,sBAAsB,CAAC,EACxD,OAAO,EAAE,SAAS,MAAM,EAAE,GACxB,SAAS,MAAM,EAAE,CAqBnB","sourcesContent":["/**\n * Minimal blocking cut: the smallest set of leaf claims whose closure would\n * unblock every blocking root, so a user sees \"fix these two\" instead of a\n * dump of every intermediate claim on the path.\n *\n * The graph is an all/any DAG, so the cut is computed by one memoized pass:\n * an `all` node needs every blocking required child's cut, an `any` node\n * needs only the cheapest child's cut (ties broken by claim id, so the\n * explanation is stable across runs), and a node that is blocked without any\n * blocking required child — a composite with its own counterexample — is its\n * own cut. Advisory children never enter a cut: they cannot block a parent.\n * Bounded by O(V + E); no general minimum-hitting-set search.\n */\n\nimport type { ClaimClosureEvaluation, ClaimNode, ClaimVerdict } from \"./claim-types.ts\";\n\nconst BLOCKING_VERDICTS: readonly ClaimVerdict[] = [\n\t\"violated\",\n\t\"stale\",\n\t\"incomplete_scope\",\n\t\"insufficient_trust\",\n\t\"missing\",\n];\n\nexport function isBlockingVerdict(verdict: ClaimVerdict): boolean {\n\treturn BLOCKING_VERDICTS.includes(verdict);\n}\n\nfunction compareCodeUnits(left: string, right: string): number {\n\tif (left === right) return 0;\n\treturn left < right ? -1 : 1;\n}\n\nfunction compareCuts(left: readonly string[], right: readonly string[]): number {\n\tif (left.length !== right.length) return left.length - right.length;\n\tfor (let index = 0; index < left.length; index++) {\n\t\tconst order = compareCodeUnits(left[index], right[index]);\n\t\tif (order !== 0) return order;\n\t}\n\treturn 0;\n}\n\nfunction unionSorted(sets: readonly (readonly string[])[]): string[] {\n\treturn [...new Set(sets.flat())].sort(compareCodeUnits);\n}\n\nexport function minimalBlockingCut(\n\tclaims: ReadonlyMap<string, ClaimNode>,\n\tevaluations: ReadonlyMap<string, ClaimClosureEvaluation>,\n\trootIds: readonly string[],\n): readonly string[] {\n\tconst memo = new Map<string, readonly string[]>();\n\tconst cutFor = (claimId: string): readonly string[] => {\n\t\tconst cached = memo.get(claimId);\n\t\tif (cached !== undefined) return cached;\n\t\tconst evaluation = evaluations.get(claimId);\n\t\tconst claim = claims.get(claimId);\n\t\tlet cut: readonly string[] = [];\n\t\tif (claim !== undefined && evaluation !== undefined && isBlockingVerdict(evaluation.verdict)) {\n\t\t\tconst childCuts = claim.satisfaction.inputs\n\t\t\t\t.filter((input) => claims.get(input)?.severity === \"required\")\n\t\t\t\t.map(cutFor)\n\t\t\t\t.filter((childCut) => childCut.length > 0);\n\t\t\tif (childCuts.length === 0) cut = [claimId];\n\t\t\telse if (claim.satisfaction.rule === \"all\") cut = unionSorted(childCuts);\n\t\t\telse cut = [...childCuts].sort(compareCuts)[0];\n\t\t}\n\t\tmemo.set(claimId, cut);\n\t\treturn cut;\n\t};\n\treturn unionSorted(rootIds.map(cutFor));\n}\n"]}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal blocking cut: the smallest set of leaf claims whose closure would
|
|
3
|
+
* unblock every blocking root, so a user sees "fix these two" instead of a
|
|
4
|
+
* dump of every intermediate claim on the path.
|
|
5
|
+
*
|
|
6
|
+
* The graph is an all/any DAG, so the cut is computed by one memoized pass:
|
|
7
|
+
* an `all` node needs every blocking required child's cut, an `any` node
|
|
8
|
+
* needs only the cheapest child's cut (ties broken by claim id, so the
|
|
9
|
+
* explanation is stable across runs), and a node that is blocked without any
|
|
10
|
+
* blocking required child — a composite with its own counterexample — is its
|
|
11
|
+
* own cut. Advisory children never enter a cut: they cannot block a parent.
|
|
12
|
+
* Bounded by O(V + E); no general minimum-hitting-set search.
|
|
13
|
+
*/
|
|
14
|
+
const BLOCKING_VERDICTS = [
|
|
15
|
+
"violated",
|
|
16
|
+
"stale",
|
|
17
|
+
"incomplete_scope",
|
|
18
|
+
"insufficient_trust",
|
|
19
|
+
"missing",
|
|
20
|
+
];
|
|
21
|
+
export function isBlockingVerdict(verdict) {
|
|
22
|
+
return BLOCKING_VERDICTS.includes(verdict);
|
|
23
|
+
}
|
|
24
|
+
function compareCodeUnits(left, right) {
|
|
25
|
+
if (left === right)
|
|
26
|
+
return 0;
|
|
27
|
+
return left < right ? -1 : 1;
|
|
28
|
+
}
|
|
29
|
+
function compareCuts(left, right) {
|
|
30
|
+
if (left.length !== right.length)
|
|
31
|
+
return left.length - right.length;
|
|
32
|
+
for (let index = 0; index < left.length; index++) {
|
|
33
|
+
const order = compareCodeUnits(left[index], right[index]);
|
|
34
|
+
if (order !== 0)
|
|
35
|
+
return order;
|
|
36
|
+
}
|
|
37
|
+
return 0;
|
|
38
|
+
}
|
|
39
|
+
function unionSorted(sets) {
|
|
40
|
+
return [...new Set(sets.flat())].sort(compareCodeUnits);
|
|
41
|
+
}
|
|
42
|
+
export function minimalBlockingCut(claims, evaluations, rootIds) {
|
|
43
|
+
const memo = new Map();
|
|
44
|
+
const cutFor = (claimId) => {
|
|
45
|
+
const cached = memo.get(claimId);
|
|
46
|
+
if (cached !== undefined)
|
|
47
|
+
return cached;
|
|
48
|
+
const evaluation = evaluations.get(claimId);
|
|
49
|
+
const claim = claims.get(claimId);
|
|
50
|
+
let cut = [];
|
|
51
|
+
if (claim !== undefined && evaluation !== undefined && isBlockingVerdict(evaluation.verdict)) {
|
|
52
|
+
const childCuts = claim.satisfaction.inputs
|
|
53
|
+
.filter((input) => claims.get(input)?.severity === "required")
|
|
54
|
+
.map(cutFor)
|
|
55
|
+
.filter((childCut) => childCut.length > 0);
|
|
56
|
+
if (childCuts.length === 0)
|
|
57
|
+
cut = [claimId];
|
|
58
|
+
else if (claim.satisfaction.rule === "all")
|
|
59
|
+
cut = unionSorted(childCuts);
|
|
60
|
+
else
|
|
61
|
+
cut = [...childCuts].sort(compareCuts)[0];
|
|
62
|
+
}
|
|
63
|
+
memo.set(claimId, cut);
|
|
64
|
+
return cut;
|
|
65
|
+
};
|
|
66
|
+
return unionSorted(rootIds.map(cutFor));
|
|
67
|
+
}
|
|
68
|
+
//# sourceMappingURL=claim-blocking-cut.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claim-blocking-cut.js","sourceRoot":"","sources":["../../src/claims/claim-blocking-cut.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Minimal blocking cut: the smallest set of leaf claims whose closure would\n * unblock every blocking root, so a user sees \"fix these two\" instead of a\n * dump of every intermediate claim on the path.\n *\n * The graph is an all/any DAG, so the cut is computed by one memoized pass:\n * an `all` node needs every blocking required child's cut, an `any` node\n * needs only the cheapest child's cut (ties broken by claim id, so the\n * explanation is stable across runs), and a node that is blocked without any\n * blocking required child — a composite with its own counterexample — is its\n * own cut. Advisory children never enter a cut: they cannot block a parent.\n * Bounded by O(V + E); no general minimum-hitting-set search.\n */\n\nimport type { ClaimClosureEvaluation, ClaimNode, ClaimVerdict } from \"./claim-types.ts\";\n\nconst BLOCKING_VERDICTS: readonly ClaimVerdict[] = [\n\t\"violated\",\n\t\"stale\",\n\t\"incomplete_scope\",\n\t\"insufficient_trust\",\n\t\"missing\",\n];\n\nexport function isBlockingVerdict(verdict: ClaimVerdict): boolean {\n\treturn BLOCKING_VERDICTS.includes(verdict);\n}\n\nfunction compareCodeUnits(left: string, right: string): number {\n\tif (left === right) return 0;\n\treturn left < right ? -1 : 1;\n}\n\nfunction compareCuts(left: readonly string[], right: readonly string[]): number {\n\tif (left.length !== right.length) return left.length - right.length;\n\tfor (let index = 0; index < left.length; index++) {\n\t\tconst order = compareCodeUnits(left[index], right[index]);\n\t\tif (order !== 0) return order;\n\t}\n\treturn 0;\n}\n\nfunction unionSorted(sets: readonly (readonly string[])[]): string[] {\n\treturn [...new Set(sets.flat())].sort(compareCodeUnits);\n}\n\nexport function minimalBlockingCut(\n\tclaims: ReadonlyMap<string, ClaimNode>,\n\tevaluations: ReadonlyMap<string, ClaimClosureEvaluation>,\n\trootIds: readonly string[],\n): readonly string[] {\n\tconst memo = new Map<string, readonly string[]>();\n\tconst cutFor = (claimId: string): readonly string[] => {\n\t\tconst cached = memo.get(claimId);\n\t\tif (cached !== undefined) return cached;\n\t\tconst evaluation = evaluations.get(claimId);\n\t\tconst claim = claims.get(claimId);\n\t\tlet cut: readonly string[] = [];\n\t\tif (claim !== undefined && evaluation !== undefined && isBlockingVerdict(evaluation.verdict)) {\n\t\t\tconst childCuts = claim.satisfaction.inputs\n\t\t\t\t.filter((input) => claims.get(input)?.severity === \"required\")\n\t\t\t\t.map(cutFor)\n\t\t\t\t.filter((childCut) => childCut.length > 0);\n\t\t\tif (childCuts.length === 0) cut = [claimId];\n\t\t\telse if (claim.satisfaction.rule === \"all\") cut = unionSorted(childCuts);\n\t\t\telse cut = [...childCuts].sort(compareCuts)[0];\n\t\t}\n\t\tmemo.set(claimId, cut);\n\t\treturn cut;\n\t};\n\treturn unionSorted(rootIds.map(cutFor));\n}\n"]}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Proof closure evaluation over a claim graph (plan §5.5).
|
|
3
|
+
*
|
|
4
|
+
* Order of operations, each step pure and deterministic:
|
|
5
|
+
*
|
|
6
|
+
* 1. Drop observations bound to another source root or environment.
|
|
7
|
+
* 2. Classify expired observations and observations missing one of a claim's
|
|
8
|
+
* invalidation keys as stale for that claim.
|
|
9
|
+
* 3. A qualified counterexample decides `violated` before any witness counts.
|
|
10
|
+
* 4. Leaf witnesses below the claim's trust floor never count; distinct
|
|
11
|
+
* independence groups must reach `requiredWitnesses`.
|
|
12
|
+
* 5. Composite claims fold their *required* children (`all` = worst child,
|
|
13
|
+
* `any` = best child) in topological order, so a parent is never read
|
|
14
|
+
* before its inputs. Advisory children are reported but never fold into a
|
|
15
|
+
* parent, and a composite with no required children is witnessed like a leaf.
|
|
16
|
+
* 6. A scope-sensitive claim is blocked while the workspace witness is not
|
|
17
|
+
* complete; an unresolved effect blocks the whole closure.
|
|
18
|
+
* 7. A waiver removes an unclosed claim from the required set. It never
|
|
19
|
+
* removes a violated one: a counterexample beats a waiver.
|
|
20
|
+
*
|
|
21
|
+
* The public verdict is decided by the required *roots* (claims nobody lists
|
|
22
|
+
* as an input): `verified` only when every required root is closed, no effect
|
|
23
|
+
* is unresolved, and the workspace witness is complete. A required leaf under
|
|
24
|
+
* an `any` parent therefore blocks nothing once a sibling branch closes — that
|
|
25
|
+
* is what `any` means — while `blockingClaimIds` still lists every required
|
|
26
|
+
* claim on a blocking path so the cause is explainable.
|
|
27
|
+
*/
|
|
28
|
+
import { type ProofClosureInput, type ProofClosureResult } from "./claim-types.ts";
|
|
29
|
+
export declare function evaluateProofClosure(input: ProofClosureInput): ProofClosureResult;
|
|
30
|
+
//# sourceMappingURL=claim-closure.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claim-closure.d.ts","sourceRoot":"","sources":["../../src/claims/claim-closure.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAIH,OAAO,EAQN,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EAGvB,MAAM,kBAAkB,CAAC;AAqJ1B,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,iBAAiB,GAAG,kBAAkB,CAiDjF","sourcesContent":["/**\n * Proof closure evaluation over a claim graph (plan §5.5).\n *\n * Order of operations, each step pure and deterministic:\n *\n * 1. Drop observations bound to another source root or environment.\n * 2. Classify expired observations and observations missing one of a claim's\n * invalidation keys as stale for that claim.\n * 3. A qualified counterexample decides `violated` before any witness counts.\n * 4. Leaf witnesses below the claim's trust floor never count; distinct\n * independence groups must reach `requiredWitnesses`.\n * 5. Composite claims fold their *required* children (`all` = worst child,\n * `any` = best child) in topological order, so a parent is never read\n * before its inputs. Advisory children are reported but never fold into a\n * parent, and a composite with no required children is witnessed like a leaf.\n * 6. A scope-sensitive claim is blocked while the workspace witness is not\n * complete; an unresolved effect blocks the whole closure.\n * 7. A waiver removes an unclosed claim from the required set. It never\n * removes a violated one: a counterexample beats a waiver.\n *\n * The public verdict is decided by the required *roots* (claims nobody lists\n * as an input): `verified` only when every required root is closed, no effect\n * is unresolved, and the workspace witness is complete. A required leaf under\n * an `any` parent therefore blocks nothing once a sibling branch closes — that\n * is what `any` means — while `blockingClaimIds` still lists every required\n * claim on a blocking path so the cause is explainable.\n */\n\nimport { isBlockingVerdict, minimalBlockingCut } from \"./claim-blocking-cut.ts\";\nimport { ClaimGraphError, rootClaimIds, topologicalClaimOrder, validateClaimGraph } from \"./claim-graph.ts\";\nimport {\n\tCLAIM_VERDICT_PRECEDENCE,\n\ttype ClaimClosureEvaluation,\n\ttype ClaimNode,\n\ttype ClaimReasonCode,\n\ttype ClaimVerdict,\n\tOBSERVATION_TRUST_RANK,\n\ttype ObservationNode,\n\ttype ProofClosureInput,\n\ttype ProofClosureResult,\n\ttype VerificationVerdict,\n\ttype WaiverNode,\n} from \"./claim-types.ts\";\n\nconst ISO_TIMESTAMP = /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3}Z$/;\n\ninterface Witnesses {\n\treadonly supporting: ObservationNode[];\n\treadonly violating: ObservationNode[];\n\treadonly stale: ObservationNode[];\n}\n\nfunction compareCodeUnits(left: string, right: string): number {\n\tif (left === right) return 0;\n\treturn left < right ? -1 : 1;\n}\n\nfunction requireTimestamp(value: string, label: string): string {\n\tif (!ISO_TIMESTAMP.test(value))\n\t\tthrow new ClaimGraphError(\"invalid_input\", `${label} must be an ISO-8601 UTC instant`);\n\treturn value;\n}\n\nfunction reasonFor(verdict: ClaimVerdict): ClaimReasonCode {\n\treturn `claim.${verdict}` as ClaimReasonCode;\n}\n\nfunction worst(verdicts: readonly ClaimVerdict[]): ClaimVerdict {\n\treturn [...verdicts].sort((a, b) => CLAIM_VERDICT_PRECEDENCE.indexOf(a) - CLAIM_VERDICT_PRECEDENCE.indexOf(b))[0];\n}\n\nfunction best(verdicts: readonly ClaimVerdict[]): ClaimVerdict {\n\treturn [...verdicts].sort((a, b) => CLAIM_VERDICT_PRECEDENCE.indexOf(b) - CLAIM_VERDICT_PRECEDENCE.indexOf(a))[0];\n}\n\n/** Steps 1–2: bind observations to claims, dropping foreign ones and marking stale ones. */\nfunction indexWitnesses(input: ProofClosureInput, claims: ReadonlyMap<string, ClaimNode>): Map<string, Witnesses> {\n\tconst index = new Map<string, Witnesses>();\n\tfor (const claim of claims.values()) index.set(claim.claimId, { supporting: [], violating: [], stale: [] });\n\tfor (const observation of input.observations) {\n\t\tif (observation.sourceRoot !== input.sourceRoot || observation.environmentDigest !== input.environmentDigest)\n\t\t\tcontinue;\n\t\tconst expired =\n\t\t\tobservation.validUntil !== undefined && requireTimestamp(observation.validUntil, \"validUntil\") <= input.now;\n\t\tconst attested = observation.invalidationKeys ?? [];\n\t\tfor (const claimId of observation.claimIds) {\n\t\t\tconst claim = claims.get(claimId);\n\t\t\tconst witnesses = index.get(claimId);\n\t\t\tif (claim === undefined || witnesses === undefined) continue;\n\t\t\tconst missingKey = claim.invalidationKeys.some((key) => !attested.includes(key));\n\t\t\tif (expired || missingKey) witnesses.stale.push(observation);\n\t\t\telse if (observation.polarity === \"violates\") witnesses.violating.push(observation);\n\t\t\telse witnesses.supporting.push(observation);\n\t\t}\n\t}\n\treturn index;\n}\n\nfunction ids(observations: readonly ObservationNode[]): readonly string[] {\n\treturn observations.map((observation) => observation.observationId).sort(compareCodeUnits);\n}\n\n/** Steps 3–4 for a leaf claim. */\nfunction evaluateLeaf(\n\tclaim: ClaimNode,\n\twitnesses: Witnesses,\n): { verdict: ClaimVerdict; observationIds: readonly string[] } {\n\tif (witnesses.violating.length > 0) return { verdict: \"violated\", observationIds: ids(witnesses.violating) };\n\tconst floor = OBSERVATION_TRUST_RANK[claim.trustFloor];\n\tconst trusted = witnesses.supporting.filter((observation) => OBSERVATION_TRUST_RANK[observation.source] >= floor);\n\tconst groups = new Set(trusted.map((observation) => observation.independenceGroup ?? observation.observationId));\n\tif (groups.size >= (claim.requiredWitnesses ?? 1)) return { verdict: \"satisfied\", observationIds: ids(trusted) };\n\tif (trusted.length > 0) return { verdict: \"missing\", observationIds: ids(trusted) };\n\tif (witnesses.supporting.length > 0)\n\t\treturn { verdict: \"insufficient_trust\", observationIds: ids(witnesses.supporting) };\n\tif (witnesses.stale.length > 0) return { verdict: \"stale\", observationIds: ids(witnesses.stale) };\n\treturn { verdict: \"missing\", observationIds: [] };\n}\n\n/** Inputs that take part in a parent's fold: advisory children are informational only. */\nfunction requiredInputs(claim: ClaimNode, claims: ReadonlyMap<string, ClaimNode>): readonly string[] {\n\treturn claim.satisfaction.inputs.filter((input) => claims.get(input)?.severity === \"required\");\n}\n\nfunction isClosed(verdict: ClaimVerdict): boolean {\n\treturn verdict === \"satisfied\" || verdict === \"waived\";\n}\n\n/** Step 5 for a composite claim; its required children are already evaluated. */\nfunction evaluateComposite(\n\tclaim: ClaimNode,\n\tinputs: readonly string[],\n\twitnesses: Witnesses,\n\tevaluations: ReadonlyMap<string, ClaimClosureEvaluation>,\n): { verdict: ClaimVerdict; observationIds: readonly string[] } {\n\tif (witnesses.violating.length > 0) return { verdict: \"violated\", observationIds: ids(witnesses.violating) };\n\tconst childVerdicts = inputs.map((input) => evaluations.get(input)?.verdict ?? \"missing\");\n\tif (claim.satisfaction.rule === \"all\") {\n\t\tconst open = childVerdicts.filter((verdict) => !isClosed(verdict));\n\t\treturn { verdict: open.length === 0 ? \"satisfied\" : worst(open), observationIds: [] };\n\t}\n\tif (childVerdicts.some(isClosed)) return { verdict: \"satisfied\", observationIds: [] };\n\treturn { verdict: best(childVerdicts), observationIds: [] };\n}\n\n/** Step 7: the lexically first valid waiver for a claim, or none. */\nfunction indexWaivers(input: ProofClosureInput, claims: ReadonlyMap<string, ClaimNode>): Map<string, WaiverNode> {\n\tconst byClaim = new Map<string, WaiverNode>();\n\tfor (const waiver of [...input.waivers].sort((a, b) => compareCodeUnits(a.waiverId, b.waiverId))) {\n\t\tif (!claims.has(waiver.claimId))\n\t\t\tthrow new ClaimGraphError(\"unknown_input\", `${waiver.waiverId} waives unknown ${waiver.claimId}`);\n\t\tif (waiver.sourceRoot !== input.sourceRoot) continue;\n\t\tif (requireTimestamp(waiver.expiresAt, \"expiresAt\") <= input.now) continue;\n\t\tif (!byClaim.has(waiver.claimId)) byClaim.set(waiver.claimId, waiver);\n\t}\n\treturn byClaim;\n}\n\n/** Required claims on a blocking path from a blocking required root, in graph order. */\nfunction blockingPathClaimIds(\n\tclaims: ReadonlyMap<string, ClaimNode>,\n\tevaluations: ReadonlyMap<string, ClaimClosureEvaluation>,\n\tblockingRoots: readonly string[],\n\tgraphOrder: readonly string[],\n): readonly string[] {\n\tconst onPath = new Set<string>();\n\tconst visit = (claimId: string): void => {\n\t\tif (onPath.has(claimId)) return;\n\t\tconst claim = claims.get(claimId);\n\t\tconst verdict = evaluations.get(claimId)?.verdict;\n\t\tif (claim === undefined || verdict === undefined || !isBlockingVerdict(verdict)) return;\n\t\tonPath.add(claimId);\n\t\tfor (const input of requiredInputs(claim, claims)) visit(input);\n\t};\n\tfor (const root of blockingRoots) visit(root);\n\treturn graphOrder.filter((claimId) => onPath.has(claimId));\n}\n\nfunction globalVerdict(\n\trequiredRoots: readonly ClaimClosureEvaluation[],\n\tinput: ProofClosureInput,\n): VerificationVerdict {\n\tif (requiredRoots.length === 0) return \"unverified\";\n\tif (requiredRoots.some((root) => root.verdict === \"violated\")) return \"violated\";\n\tconst closure =\n\t\trequiredRoots.every((root) => isClosed(root.verdict)) &&\n\t\tinput.unresolvedEffectIds.length === 0 &&\n\t\tinput.workspaceCompleteness === \"complete\";\n\treturn closure ? \"verified\" : \"inconclusive\";\n}\n\nexport function evaluateProofClosure(input: ProofClosureInput): ProofClosureResult {\n\tconst claims = validateClaimGraph(input.graph);\n\trequireTimestamp(input.now, \"now\");\n\tconst witnesses = indexWitnesses(input, claims);\n\tconst waivers = indexWaivers(input, claims);\n\tconst evaluations = new Map<string, ClaimClosureEvaluation>();\n\tfor (const claim of topologicalClaimOrder(input.graph, claims)) {\n\t\tconst claimWitnesses = witnesses.get(claim.claimId) as Witnesses;\n\t\tconst inputs = requiredInputs(claim, claims);\n\t\tconst local =\n\t\t\tinputs.length === 0\n\t\t\t\t? evaluateLeaf(claim, claimWitnesses)\n\t\t\t\t: evaluateComposite(claim, inputs, claimWitnesses, evaluations);\n\t\tlet verdict = local.verdict;\n\t\tif (verdict !== \"violated\" && claim.scopeSensitive === true && input.workspaceCompleteness !== \"complete\") {\n\t\t\tverdict = \"incomplete_scope\";\n\t\t}\n\t\tconst waiver = waivers.get(claim.claimId);\n\t\tconst waived = waiver !== undefined && verdict !== \"violated\" && verdict !== \"satisfied\";\n\t\tif (waived) verdict = \"waived\";\n\t\tevaluations.set(\n\t\t\tclaim.claimId,\n\t\t\tObject.freeze({\n\t\t\t\tclaimId: claim.claimId,\n\t\t\t\tseverity: claim.severity,\n\t\t\t\tverdict,\n\t\t\t\treasonCode: reasonFor(verdict),\n\t\t\t\tobservationIds: Object.freeze([...local.observationIds]),\n\t\t\t\t...(waived ? { waiverId: waiver.waiverId } : {}),\n\t\t\t}),\n\t\t);\n\t}\n\tconst graphOrder = input.graph.claims.map((claim) => claim.claimId);\n\tconst ordered = graphOrder.map((claimId) => evaluations.get(claimId) as ClaimClosureEvaluation);\n\tconst requiredRoots = rootClaimIds(input.graph)\n\t\t.map((claimId) => evaluations.get(claimId) as ClaimClosureEvaluation)\n\t\t.filter((evaluation) => evaluation.severity === \"required\");\n\tconst blockingRoots = requiredRoots\n\t\t.filter((evaluation) => isBlockingVerdict(evaluation.verdict))\n\t\t.map((evaluation) => evaluation.claimId);\n\tconst blockingClaimIds = blockingPathClaimIds(claims, evaluations, blockingRoots, graphOrder);\n\treturn Object.freeze({\n\t\tverdict: globalVerdict(requiredRoots, input),\n\t\tclaimEvaluations: Object.freeze(ordered),\n\t\tblockingClaimIds: Object.freeze(blockingClaimIds),\n\t\tminimalBlockingCut: Object.freeze([...minimalBlockingCut(claims, evaluations, blockingRoots)]),\n\t\tunresolvedEffectIds: Object.freeze([...input.unresolvedEffectIds].sort(compareCodeUnits)),\n\t\tworkspaceCompleteness: input.workspaceCompleteness,\n\t});\n}\n"]}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Proof closure evaluation over a claim graph (plan §5.5).
|
|
3
|
+
*
|
|
4
|
+
* Order of operations, each step pure and deterministic:
|
|
5
|
+
*
|
|
6
|
+
* 1. Drop observations bound to another source root or environment.
|
|
7
|
+
* 2. Classify expired observations and observations missing one of a claim's
|
|
8
|
+
* invalidation keys as stale for that claim.
|
|
9
|
+
* 3. A qualified counterexample decides `violated` before any witness counts.
|
|
10
|
+
* 4. Leaf witnesses below the claim's trust floor never count; distinct
|
|
11
|
+
* independence groups must reach `requiredWitnesses`.
|
|
12
|
+
* 5. Composite claims fold their *required* children (`all` = worst child,
|
|
13
|
+
* `any` = best child) in topological order, so a parent is never read
|
|
14
|
+
* before its inputs. Advisory children are reported but never fold into a
|
|
15
|
+
* parent, and a composite with no required children is witnessed like a leaf.
|
|
16
|
+
* 6. A scope-sensitive claim is blocked while the workspace witness is not
|
|
17
|
+
* complete; an unresolved effect blocks the whole closure.
|
|
18
|
+
* 7. A waiver removes an unclosed claim from the required set. It never
|
|
19
|
+
* removes a violated one: a counterexample beats a waiver.
|
|
20
|
+
*
|
|
21
|
+
* The public verdict is decided by the required *roots* (claims nobody lists
|
|
22
|
+
* as an input): `verified` only when every required root is closed, no effect
|
|
23
|
+
* is unresolved, and the workspace witness is complete. A required leaf under
|
|
24
|
+
* an `any` parent therefore blocks nothing once a sibling branch closes — that
|
|
25
|
+
* is what `any` means — while `blockingClaimIds` still lists every required
|
|
26
|
+
* claim on a blocking path so the cause is explainable.
|
|
27
|
+
*/
|
|
28
|
+
import { isBlockingVerdict, minimalBlockingCut } from "./claim-blocking-cut.js";
|
|
29
|
+
import { ClaimGraphError, rootClaimIds, topologicalClaimOrder, validateClaimGraph } from "./claim-graph.js";
|
|
30
|
+
import { CLAIM_VERDICT_PRECEDENCE, OBSERVATION_TRUST_RANK, } from "./claim-types.js";
|
|
31
|
+
const ISO_TIMESTAMP = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;
|
|
32
|
+
function compareCodeUnits(left, right) {
|
|
33
|
+
if (left === right)
|
|
34
|
+
return 0;
|
|
35
|
+
return left < right ? -1 : 1;
|
|
36
|
+
}
|
|
37
|
+
function requireTimestamp(value, label) {
|
|
38
|
+
if (!ISO_TIMESTAMP.test(value))
|
|
39
|
+
throw new ClaimGraphError("invalid_input", `${label} must be an ISO-8601 UTC instant`);
|
|
40
|
+
return value;
|
|
41
|
+
}
|
|
42
|
+
function reasonFor(verdict) {
|
|
43
|
+
return `claim.${verdict}`;
|
|
44
|
+
}
|
|
45
|
+
function worst(verdicts) {
|
|
46
|
+
return [...verdicts].sort((a, b) => CLAIM_VERDICT_PRECEDENCE.indexOf(a) - CLAIM_VERDICT_PRECEDENCE.indexOf(b))[0];
|
|
47
|
+
}
|
|
48
|
+
function best(verdicts) {
|
|
49
|
+
return [...verdicts].sort((a, b) => CLAIM_VERDICT_PRECEDENCE.indexOf(b) - CLAIM_VERDICT_PRECEDENCE.indexOf(a))[0];
|
|
50
|
+
}
|
|
51
|
+
/** Steps 1–2: bind observations to claims, dropping foreign ones and marking stale ones. */
|
|
52
|
+
function indexWitnesses(input, claims) {
|
|
53
|
+
const index = new Map();
|
|
54
|
+
for (const claim of claims.values())
|
|
55
|
+
index.set(claim.claimId, { supporting: [], violating: [], stale: [] });
|
|
56
|
+
for (const observation of input.observations) {
|
|
57
|
+
if (observation.sourceRoot !== input.sourceRoot || observation.environmentDigest !== input.environmentDigest)
|
|
58
|
+
continue;
|
|
59
|
+
const expired = observation.validUntil !== undefined && requireTimestamp(observation.validUntil, "validUntil") <= input.now;
|
|
60
|
+
const attested = observation.invalidationKeys ?? [];
|
|
61
|
+
for (const claimId of observation.claimIds) {
|
|
62
|
+
const claim = claims.get(claimId);
|
|
63
|
+
const witnesses = index.get(claimId);
|
|
64
|
+
if (claim === undefined || witnesses === undefined)
|
|
65
|
+
continue;
|
|
66
|
+
const missingKey = claim.invalidationKeys.some((key) => !attested.includes(key));
|
|
67
|
+
if (expired || missingKey)
|
|
68
|
+
witnesses.stale.push(observation);
|
|
69
|
+
else if (observation.polarity === "violates")
|
|
70
|
+
witnesses.violating.push(observation);
|
|
71
|
+
else
|
|
72
|
+
witnesses.supporting.push(observation);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
return index;
|
|
76
|
+
}
|
|
77
|
+
function ids(observations) {
|
|
78
|
+
return observations.map((observation) => observation.observationId).sort(compareCodeUnits);
|
|
79
|
+
}
|
|
80
|
+
/** Steps 3–4 for a leaf claim. */
|
|
81
|
+
function evaluateLeaf(claim, witnesses) {
|
|
82
|
+
if (witnesses.violating.length > 0)
|
|
83
|
+
return { verdict: "violated", observationIds: ids(witnesses.violating) };
|
|
84
|
+
const floor = OBSERVATION_TRUST_RANK[claim.trustFloor];
|
|
85
|
+
const trusted = witnesses.supporting.filter((observation) => OBSERVATION_TRUST_RANK[observation.source] >= floor);
|
|
86
|
+
const groups = new Set(trusted.map((observation) => observation.independenceGroup ?? observation.observationId));
|
|
87
|
+
if (groups.size >= (claim.requiredWitnesses ?? 1))
|
|
88
|
+
return { verdict: "satisfied", observationIds: ids(trusted) };
|
|
89
|
+
if (trusted.length > 0)
|
|
90
|
+
return { verdict: "missing", observationIds: ids(trusted) };
|
|
91
|
+
if (witnesses.supporting.length > 0)
|
|
92
|
+
return { verdict: "insufficient_trust", observationIds: ids(witnesses.supporting) };
|
|
93
|
+
if (witnesses.stale.length > 0)
|
|
94
|
+
return { verdict: "stale", observationIds: ids(witnesses.stale) };
|
|
95
|
+
return { verdict: "missing", observationIds: [] };
|
|
96
|
+
}
|
|
97
|
+
/** Inputs that take part in a parent's fold: advisory children are informational only. */
|
|
98
|
+
function requiredInputs(claim, claims) {
|
|
99
|
+
return claim.satisfaction.inputs.filter((input) => claims.get(input)?.severity === "required");
|
|
100
|
+
}
|
|
101
|
+
function isClosed(verdict) {
|
|
102
|
+
return verdict === "satisfied" || verdict === "waived";
|
|
103
|
+
}
|
|
104
|
+
/** Step 5 for a composite claim; its required children are already evaluated. */
|
|
105
|
+
function evaluateComposite(claim, inputs, witnesses, evaluations) {
|
|
106
|
+
if (witnesses.violating.length > 0)
|
|
107
|
+
return { verdict: "violated", observationIds: ids(witnesses.violating) };
|
|
108
|
+
const childVerdicts = inputs.map((input) => evaluations.get(input)?.verdict ?? "missing");
|
|
109
|
+
if (claim.satisfaction.rule === "all") {
|
|
110
|
+
const open = childVerdicts.filter((verdict) => !isClosed(verdict));
|
|
111
|
+
return { verdict: open.length === 0 ? "satisfied" : worst(open), observationIds: [] };
|
|
112
|
+
}
|
|
113
|
+
if (childVerdicts.some(isClosed))
|
|
114
|
+
return { verdict: "satisfied", observationIds: [] };
|
|
115
|
+
return { verdict: best(childVerdicts), observationIds: [] };
|
|
116
|
+
}
|
|
117
|
+
/** Step 7: the lexically first valid waiver for a claim, or none. */
|
|
118
|
+
function indexWaivers(input, claims) {
|
|
119
|
+
const byClaim = new Map();
|
|
120
|
+
for (const waiver of [...input.waivers].sort((a, b) => compareCodeUnits(a.waiverId, b.waiverId))) {
|
|
121
|
+
if (!claims.has(waiver.claimId))
|
|
122
|
+
throw new ClaimGraphError("unknown_input", `${waiver.waiverId} waives unknown ${waiver.claimId}`);
|
|
123
|
+
if (waiver.sourceRoot !== input.sourceRoot)
|
|
124
|
+
continue;
|
|
125
|
+
if (requireTimestamp(waiver.expiresAt, "expiresAt") <= input.now)
|
|
126
|
+
continue;
|
|
127
|
+
if (!byClaim.has(waiver.claimId))
|
|
128
|
+
byClaim.set(waiver.claimId, waiver);
|
|
129
|
+
}
|
|
130
|
+
return byClaim;
|
|
131
|
+
}
|
|
132
|
+
/** Required claims on a blocking path from a blocking required root, in graph order. */
|
|
133
|
+
function blockingPathClaimIds(claims, evaluations, blockingRoots, graphOrder) {
|
|
134
|
+
const onPath = new Set();
|
|
135
|
+
const visit = (claimId) => {
|
|
136
|
+
if (onPath.has(claimId))
|
|
137
|
+
return;
|
|
138
|
+
const claim = claims.get(claimId);
|
|
139
|
+
const verdict = evaluations.get(claimId)?.verdict;
|
|
140
|
+
if (claim === undefined || verdict === undefined || !isBlockingVerdict(verdict))
|
|
141
|
+
return;
|
|
142
|
+
onPath.add(claimId);
|
|
143
|
+
for (const input of requiredInputs(claim, claims))
|
|
144
|
+
visit(input);
|
|
145
|
+
};
|
|
146
|
+
for (const root of blockingRoots)
|
|
147
|
+
visit(root);
|
|
148
|
+
return graphOrder.filter((claimId) => onPath.has(claimId));
|
|
149
|
+
}
|
|
150
|
+
function globalVerdict(requiredRoots, input) {
|
|
151
|
+
if (requiredRoots.length === 0)
|
|
152
|
+
return "unverified";
|
|
153
|
+
if (requiredRoots.some((root) => root.verdict === "violated"))
|
|
154
|
+
return "violated";
|
|
155
|
+
const closure = requiredRoots.every((root) => isClosed(root.verdict)) &&
|
|
156
|
+
input.unresolvedEffectIds.length === 0 &&
|
|
157
|
+
input.workspaceCompleteness === "complete";
|
|
158
|
+
return closure ? "verified" : "inconclusive";
|
|
159
|
+
}
|
|
160
|
+
export function evaluateProofClosure(input) {
|
|
161
|
+
const claims = validateClaimGraph(input.graph);
|
|
162
|
+
requireTimestamp(input.now, "now");
|
|
163
|
+
const witnesses = indexWitnesses(input, claims);
|
|
164
|
+
const waivers = indexWaivers(input, claims);
|
|
165
|
+
const evaluations = new Map();
|
|
166
|
+
for (const claim of topologicalClaimOrder(input.graph, claims)) {
|
|
167
|
+
const claimWitnesses = witnesses.get(claim.claimId);
|
|
168
|
+
const inputs = requiredInputs(claim, claims);
|
|
169
|
+
const local = inputs.length === 0
|
|
170
|
+
? evaluateLeaf(claim, claimWitnesses)
|
|
171
|
+
: evaluateComposite(claim, inputs, claimWitnesses, evaluations);
|
|
172
|
+
let verdict = local.verdict;
|
|
173
|
+
if (verdict !== "violated" && claim.scopeSensitive === true && input.workspaceCompleteness !== "complete") {
|
|
174
|
+
verdict = "incomplete_scope";
|
|
175
|
+
}
|
|
176
|
+
const waiver = waivers.get(claim.claimId);
|
|
177
|
+
const waived = waiver !== undefined && verdict !== "violated" && verdict !== "satisfied";
|
|
178
|
+
if (waived)
|
|
179
|
+
verdict = "waived";
|
|
180
|
+
evaluations.set(claim.claimId, Object.freeze({
|
|
181
|
+
claimId: claim.claimId,
|
|
182
|
+
severity: claim.severity,
|
|
183
|
+
verdict,
|
|
184
|
+
reasonCode: reasonFor(verdict),
|
|
185
|
+
observationIds: Object.freeze([...local.observationIds]),
|
|
186
|
+
...(waived ? { waiverId: waiver.waiverId } : {}),
|
|
187
|
+
}));
|
|
188
|
+
}
|
|
189
|
+
const graphOrder = input.graph.claims.map((claim) => claim.claimId);
|
|
190
|
+
const ordered = graphOrder.map((claimId) => evaluations.get(claimId));
|
|
191
|
+
const requiredRoots = rootClaimIds(input.graph)
|
|
192
|
+
.map((claimId) => evaluations.get(claimId))
|
|
193
|
+
.filter((evaluation) => evaluation.severity === "required");
|
|
194
|
+
const blockingRoots = requiredRoots
|
|
195
|
+
.filter((evaluation) => isBlockingVerdict(evaluation.verdict))
|
|
196
|
+
.map((evaluation) => evaluation.claimId);
|
|
197
|
+
const blockingClaimIds = blockingPathClaimIds(claims, evaluations, blockingRoots, graphOrder);
|
|
198
|
+
return Object.freeze({
|
|
199
|
+
verdict: globalVerdict(requiredRoots, input),
|
|
200
|
+
claimEvaluations: Object.freeze(ordered),
|
|
201
|
+
blockingClaimIds: Object.freeze(blockingClaimIds),
|
|
202
|
+
minimalBlockingCut: Object.freeze([...minimalBlockingCut(claims, evaluations, blockingRoots)]),
|
|
203
|
+
unresolvedEffectIds: Object.freeze([...input.unresolvedEffectIds].sort(compareCodeUnits)),
|
|
204
|
+
workspaceCompleteness: input.workspaceCompleteness,
|
|
205
|
+
});
|
|
206
|
+
}
|
|
207
|
+
//# sourceMappingURL=claim-closure.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claim-closure.js","sourceRoot":"","sources":["../../src/claims/claim-closure.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Proof closure evaluation over a claim graph (plan §5.5).\n *\n * Order of operations, each step pure and deterministic:\n *\n * 1. Drop observations bound to another source root or environment.\n * 2. Classify expired observations and observations missing one of a claim's\n * invalidation keys as stale for that claim.\n * 3. A qualified counterexample decides `violated` before any witness counts.\n * 4. Leaf witnesses below the claim's trust floor never count; distinct\n * independence groups must reach `requiredWitnesses`.\n * 5. Composite claims fold their *required* children (`all` = worst child,\n * `any` = best child) in topological order, so a parent is never read\n * before its inputs. Advisory children are reported but never fold into a\n * parent, and a composite with no required children is witnessed like a leaf.\n * 6. A scope-sensitive claim is blocked while the workspace witness is not\n * complete; an unresolved effect blocks the whole closure.\n * 7. A waiver removes an unclosed claim from the required set. It never\n * removes a violated one: a counterexample beats a waiver.\n *\n * The public verdict is decided by the required *roots* (claims nobody lists\n * as an input): `verified` only when every required root is closed, no effect\n * is unresolved, and the workspace witness is complete. A required leaf under\n * an `any` parent therefore blocks nothing once a sibling branch closes — that\n * is what `any` means — while `blockingClaimIds` still lists every required\n * claim on a blocking path so the cause is explainable.\n */\n\nimport { isBlockingVerdict, minimalBlockingCut } from \"./claim-blocking-cut.ts\";\nimport { ClaimGraphError, rootClaimIds, topologicalClaimOrder, validateClaimGraph } from \"./claim-graph.ts\";\nimport {\n\tCLAIM_VERDICT_PRECEDENCE,\n\ttype ClaimClosureEvaluation,\n\ttype ClaimNode,\n\ttype ClaimReasonCode,\n\ttype ClaimVerdict,\n\tOBSERVATION_TRUST_RANK,\n\ttype ObservationNode,\n\ttype ProofClosureInput,\n\ttype ProofClosureResult,\n\ttype VerificationVerdict,\n\ttype WaiverNode,\n} from \"./claim-types.ts\";\n\nconst ISO_TIMESTAMP = /^\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}\\.\\d{3}Z$/;\n\ninterface Witnesses {\n\treadonly supporting: ObservationNode[];\n\treadonly violating: ObservationNode[];\n\treadonly stale: ObservationNode[];\n}\n\nfunction compareCodeUnits(left: string, right: string): number {\n\tif (left === right) return 0;\n\treturn left < right ? -1 : 1;\n}\n\nfunction requireTimestamp(value: string, label: string): string {\n\tif (!ISO_TIMESTAMP.test(value))\n\t\tthrow new ClaimGraphError(\"invalid_input\", `${label} must be an ISO-8601 UTC instant`);\n\treturn value;\n}\n\nfunction reasonFor(verdict: ClaimVerdict): ClaimReasonCode {\n\treturn `claim.${verdict}` as ClaimReasonCode;\n}\n\nfunction worst(verdicts: readonly ClaimVerdict[]): ClaimVerdict {\n\treturn [...verdicts].sort((a, b) => CLAIM_VERDICT_PRECEDENCE.indexOf(a) - CLAIM_VERDICT_PRECEDENCE.indexOf(b))[0];\n}\n\nfunction best(verdicts: readonly ClaimVerdict[]): ClaimVerdict {\n\treturn [...verdicts].sort((a, b) => CLAIM_VERDICT_PRECEDENCE.indexOf(b) - CLAIM_VERDICT_PRECEDENCE.indexOf(a))[0];\n}\n\n/** Steps 1–2: bind observations to claims, dropping foreign ones and marking stale ones. */\nfunction indexWitnesses(input: ProofClosureInput, claims: ReadonlyMap<string, ClaimNode>): Map<string, Witnesses> {\n\tconst index = new Map<string, Witnesses>();\n\tfor (const claim of claims.values()) index.set(claim.claimId, { supporting: [], violating: [], stale: [] });\n\tfor (const observation of input.observations) {\n\t\tif (observation.sourceRoot !== input.sourceRoot || observation.environmentDigest !== input.environmentDigest)\n\t\t\tcontinue;\n\t\tconst expired =\n\t\t\tobservation.validUntil !== undefined && requireTimestamp(observation.validUntil, \"validUntil\") <= input.now;\n\t\tconst attested = observation.invalidationKeys ?? [];\n\t\tfor (const claimId of observation.claimIds) {\n\t\t\tconst claim = claims.get(claimId);\n\t\t\tconst witnesses = index.get(claimId);\n\t\t\tif (claim === undefined || witnesses === undefined) continue;\n\t\t\tconst missingKey = claim.invalidationKeys.some((key) => !attested.includes(key));\n\t\t\tif (expired || missingKey) witnesses.stale.push(observation);\n\t\t\telse if (observation.polarity === \"violates\") witnesses.violating.push(observation);\n\t\t\telse witnesses.supporting.push(observation);\n\t\t}\n\t}\n\treturn index;\n}\n\nfunction ids(observations: readonly ObservationNode[]): readonly string[] {\n\treturn observations.map((observation) => observation.observationId).sort(compareCodeUnits);\n}\n\n/** Steps 3–4 for a leaf claim. */\nfunction evaluateLeaf(\n\tclaim: ClaimNode,\n\twitnesses: Witnesses,\n): { verdict: ClaimVerdict; observationIds: readonly string[] } {\n\tif (witnesses.violating.length > 0) return { verdict: \"violated\", observationIds: ids(witnesses.violating) };\n\tconst floor = OBSERVATION_TRUST_RANK[claim.trustFloor];\n\tconst trusted = witnesses.supporting.filter((observation) => OBSERVATION_TRUST_RANK[observation.source] >= floor);\n\tconst groups = new Set(trusted.map((observation) => observation.independenceGroup ?? observation.observationId));\n\tif (groups.size >= (claim.requiredWitnesses ?? 1)) return { verdict: \"satisfied\", observationIds: ids(trusted) };\n\tif (trusted.length > 0) return { verdict: \"missing\", observationIds: ids(trusted) };\n\tif (witnesses.supporting.length > 0)\n\t\treturn { verdict: \"insufficient_trust\", observationIds: ids(witnesses.supporting) };\n\tif (witnesses.stale.length > 0) return { verdict: \"stale\", observationIds: ids(witnesses.stale) };\n\treturn { verdict: \"missing\", observationIds: [] };\n}\n\n/** Inputs that take part in a parent's fold: advisory children are informational only. */\nfunction requiredInputs(claim: ClaimNode, claims: ReadonlyMap<string, ClaimNode>): readonly string[] {\n\treturn claim.satisfaction.inputs.filter((input) => claims.get(input)?.severity === \"required\");\n}\n\nfunction isClosed(verdict: ClaimVerdict): boolean {\n\treturn verdict === \"satisfied\" || verdict === \"waived\";\n}\n\n/** Step 5 for a composite claim; its required children are already evaluated. */\nfunction evaluateComposite(\n\tclaim: ClaimNode,\n\tinputs: readonly string[],\n\twitnesses: Witnesses,\n\tevaluations: ReadonlyMap<string, ClaimClosureEvaluation>,\n): { verdict: ClaimVerdict; observationIds: readonly string[] } {\n\tif (witnesses.violating.length > 0) return { verdict: \"violated\", observationIds: ids(witnesses.violating) };\n\tconst childVerdicts = inputs.map((input) => evaluations.get(input)?.verdict ?? \"missing\");\n\tif (claim.satisfaction.rule === \"all\") {\n\t\tconst open = childVerdicts.filter((verdict) => !isClosed(verdict));\n\t\treturn { verdict: open.length === 0 ? \"satisfied\" : worst(open), observationIds: [] };\n\t}\n\tif (childVerdicts.some(isClosed)) return { verdict: \"satisfied\", observationIds: [] };\n\treturn { verdict: best(childVerdicts), observationIds: [] };\n}\n\n/** Step 7: the lexically first valid waiver for a claim, or none. */\nfunction indexWaivers(input: ProofClosureInput, claims: ReadonlyMap<string, ClaimNode>): Map<string, WaiverNode> {\n\tconst byClaim = new Map<string, WaiverNode>();\n\tfor (const waiver of [...input.waivers].sort((a, b) => compareCodeUnits(a.waiverId, b.waiverId))) {\n\t\tif (!claims.has(waiver.claimId))\n\t\t\tthrow new ClaimGraphError(\"unknown_input\", `${waiver.waiverId} waives unknown ${waiver.claimId}`);\n\t\tif (waiver.sourceRoot !== input.sourceRoot) continue;\n\t\tif (requireTimestamp(waiver.expiresAt, \"expiresAt\") <= input.now) continue;\n\t\tif (!byClaim.has(waiver.claimId)) byClaim.set(waiver.claimId, waiver);\n\t}\n\treturn byClaim;\n}\n\n/** Required claims on a blocking path from a blocking required root, in graph order. */\nfunction blockingPathClaimIds(\n\tclaims: ReadonlyMap<string, ClaimNode>,\n\tevaluations: ReadonlyMap<string, ClaimClosureEvaluation>,\n\tblockingRoots: readonly string[],\n\tgraphOrder: readonly string[],\n): readonly string[] {\n\tconst onPath = new Set<string>();\n\tconst visit = (claimId: string): void => {\n\t\tif (onPath.has(claimId)) return;\n\t\tconst claim = claims.get(claimId);\n\t\tconst verdict = evaluations.get(claimId)?.verdict;\n\t\tif (claim === undefined || verdict === undefined || !isBlockingVerdict(verdict)) return;\n\t\tonPath.add(claimId);\n\t\tfor (const input of requiredInputs(claim, claims)) visit(input);\n\t};\n\tfor (const root of blockingRoots) visit(root);\n\treturn graphOrder.filter((claimId) => onPath.has(claimId));\n}\n\nfunction globalVerdict(\n\trequiredRoots: readonly ClaimClosureEvaluation[],\n\tinput: ProofClosureInput,\n): VerificationVerdict {\n\tif (requiredRoots.length === 0) return \"unverified\";\n\tif (requiredRoots.some((root) => root.verdict === \"violated\")) return \"violated\";\n\tconst closure =\n\t\trequiredRoots.every((root) => isClosed(root.verdict)) &&\n\t\tinput.unresolvedEffectIds.length === 0 &&\n\t\tinput.workspaceCompleteness === \"complete\";\n\treturn closure ? \"verified\" : \"inconclusive\";\n}\n\nexport function evaluateProofClosure(input: ProofClosureInput): ProofClosureResult {\n\tconst claims = validateClaimGraph(input.graph);\n\trequireTimestamp(input.now, \"now\");\n\tconst witnesses = indexWitnesses(input, claims);\n\tconst waivers = indexWaivers(input, claims);\n\tconst evaluations = new Map<string, ClaimClosureEvaluation>();\n\tfor (const claim of topologicalClaimOrder(input.graph, claims)) {\n\t\tconst claimWitnesses = witnesses.get(claim.claimId) as Witnesses;\n\t\tconst inputs = requiredInputs(claim, claims);\n\t\tconst local =\n\t\t\tinputs.length === 0\n\t\t\t\t? evaluateLeaf(claim, claimWitnesses)\n\t\t\t\t: evaluateComposite(claim, inputs, claimWitnesses, evaluations);\n\t\tlet verdict = local.verdict;\n\t\tif (verdict !== \"violated\" && claim.scopeSensitive === true && input.workspaceCompleteness !== \"complete\") {\n\t\t\tverdict = \"incomplete_scope\";\n\t\t}\n\t\tconst waiver = waivers.get(claim.claimId);\n\t\tconst waived = waiver !== undefined && verdict !== \"violated\" && verdict !== \"satisfied\";\n\t\tif (waived) verdict = \"waived\";\n\t\tevaluations.set(\n\t\t\tclaim.claimId,\n\t\t\tObject.freeze({\n\t\t\t\tclaimId: claim.claimId,\n\t\t\t\tseverity: claim.severity,\n\t\t\t\tverdict,\n\t\t\t\treasonCode: reasonFor(verdict),\n\t\t\t\tobservationIds: Object.freeze([...local.observationIds]),\n\t\t\t\t...(waived ? { waiverId: waiver.waiverId } : {}),\n\t\t\t}),\n\t\t);\n\t}\n\tconst graphOrder = input.graph.claims.map((claim) => claim.claimId);\n\tconst ordered = graphOrder.map((claimId) => evaluations.get(claimId) as ClaimClosureEvaluation);\n\tconst requiredRoots = rootClaimIds(input.graph)\n\t\t.map((claimId) => evaluations.get(claimId) as ClaimClosureEvaluation)\n\t\t.filter((evaluation) => evaluation.severity === \"required\");\n\tconst blockingRoots = requiredRoots\n\t\t.filter((evaluation) => isBlockingVerdict(evaluation.verdict))\n\t\t.map((evaluation) => evaluation.claimId);\n\tconst blockingClaimIds = blockingPathClaimIds(claims, evaluations, blockingRoots, graphOrder);\n\treturn Object.freeze({\n\t\tverdict: globalVerdict(requiredRoots, input),\n\t\tclaimEvaluations: Object.freeze(ordered),\n\t\tblockingClaimIds: Object.freeze(blockingClaimIds),\n\t\tminimalBlockingCut: Object.freeze([...minimalBlockingCut(claims, evaluations, blockingRoots)]),\n\t\tunresolvedEffectIds: Object.freeze([...input.unresolvedEffectIds].sort(compareCodeUnits)),\n\t\tworkspaceCompleteness: input.workspaceCompleteness,\n\t});\n}\n"]}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structural rules for a claim graph: identity, references, acyclicity, and
|
|
3
|
+
* a deterministic evaluation order.
|
|
4
|
+
*
|
|
5
|
+
* Validation throws `ClaimGraphError` because a malformed graph is a
|
|
6
|
+
* programming or configuration defect, not an evaluation outcome; a proof
|
|
7
|
+
* must never report `inconclusive` for a graph it could not even read.
|
|
8
|
+
*
|
|
9
|
+
* `canonicalClaimGraph` returns the graph in a normalized shape (claims by
|
|
10
|
+
* id, inputs and keys sorted) so that any layer with a hasher can derive the
|
|
11
|
+
* graph digest the plan binds into operations, receipts, and snapshots.
|
|
12
|
+
* This package deliberately owns the normalization and not the hash.
|
|
13
|
+
*/
|
|
14
|
+
import { type ClaimGraph, type ClaimNode } from "./claim-types.ts";
|
|
15
|
+
export declare class ClaimGraphError extends Error {
|
|
16
|
+
readonly code: "invalid_schema" | "invalid_claim" | "duplicate_claim" | "unknown_input" | "cycle" | "invalid_input";
|
|
17
|
+
constructor(code: ClaimGraphError["code"], message: string);
|
|
18
|
+
}
|
|
19
|
+
/** Throws on the first structural defect; returns the claims indexed by id otherwise. */
|
|
20
|
+
export declare function validateClaimGraph(graph: ClaimGraph): ReadonlyMap<string, ClaimNode>;
|
|
21
|
+
/**
|
|
22
|
+
* Children before parents, ties broken by claim id, so evaluation order is a
|
|
23
|
+
* function of the graph alone. Kahn's algorithm; leftover nodes form a cycle.
|
|
24
|
+
*/
|
|
25
|
+
export declare function topologicalClaimOrder(graph: ClaimGraph, index?: ReadonlyMap<string, ClaimNode>): readonly ClaimNode[];
|
|
26
|
+
/** Claims that are nobody's input: the roots a public verdict is reported against. */
|
|
27
|
+
export declare function rootClaimIds(graph: ClaimGraph): readonly string[];
|
|
28
|
+
/** Normalized graph for digesting: deterministic member order regardless of input order. */
|
|
29
|
+
export declare function canonicalClaimGraph(graph: ClaimGraph): ClaimGraph;
|
|
30
|
+
//# sourceMappingURL=claim-graph.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claim-graph.d.ts","sourceRoot":"","sources":["../../src/claims/claim-graph.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAA8B,KAAK,UAAU,EAAE,KAAK,SAAS,EAA0B,MAAM,kBAAkB,CAAC;AAEvH,qBAAa,eAAgB,SAAQ,KAAK;IACzC,SAAgB,IAAI,EACjB,gBAAgB,GAChB,eAAe,GACf,iBAAiB,GACjB,eAAe,GACf,OAAO,GACP,eAAe,CAAC;IAEnB,YAAY,IAAI,EAAE,eAAe,CAAC,MAAM,CAAC,EAAE,OAAO,EAAE,MAAM,EAIzD;CACD;AAkCD,yFAAyF;AACzF,wBAAgB,kBAAkB,CAAC,KAAK,EAAE,UAAU,GAAG,WAAW,CAAC,MAAM,EAAE,SAAS,CAAC,CAkBpF;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,KAAK,EAAE,UAAU,EAAE,KAAK,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,SAAS,CAAC,GAAG,SAAS,SAAS,EAAE,CAqCrH;AAQD,sFAAsF;AACtF,wBAAgB,YAAY,CAAC,KAAK,EAAE,UAAU,GAAG,SAAS,MAAM,EAAE,CAMjE;AAED,4FAA4F;AAC5F,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,UAAU,GAAG,UAAU,CAejE","sourcesContent":["/**\n * Structural rules for a claim graph: identity, references, acyclicity, and\n * a deterministic evaluation order.\n *\n * Validation throws `ClaimGraphError` because a malformed graph is a\n * programming or configuration defect, not an evaluation outcome; a proof\n * must never report `inconclusive` for a graph it could not even read.\n *\n * `canonicalClaimGraph` returns the graph in a normalized shape (claims by\n * id, inputs and keys sorted) so that any layer with a hasher can derive the\n * graph digest the plan binds into operations, receipts, and snapshots.\n * This package deliberately owns the normalization and not the hash.\n */\n\nimport { CLAIM_GRAPH_SCHEMA_VERSION, type ClaimGraph, type ClaimNode, OBSERVATION_TRUST_RANK } from \"./claim-types.ts\";\n\nexport class ClaimGraphError extends Error {\n\tpublic readonly code:\n\t\t| \"invalid_schema\"\n\t\t| \"invalid_claim\"\n\t\t| \"duplicate_claim\"\n\t\t| \"unknown_input\"\n\t\t| \"cycle\"\n\t\t| \"invalid_input\";\n\n\tconstructor(code: ClaimGraphError[\"code\"], message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"ClaimGraphError\";\n\t\tthis.code = code;\n\t}\n}\n\nfunction compareCodeUnits(left: string, right: string): number {\n\tif (left === right) return 0;\n\treturn left < right ? -1 : 1;\n}\n\nfunction isNonEmptyString(value: unknown): value is string {\n\treturn typeof value === \"string\" && value.trim().length > 0;\n}\n\nfunction validateClaim(claim: ClaimNode): void {\n\tconst id = claim.claimId;\n\tif (!isNonEmptyString(id)) throw new ClaimGraphError(\"invalid_claim\", \"claimId must be a non-empty string\");\n\tif (!isNonEmptyString(claim.statement)) throw new ClaimGraphError(\"invalid_claim\", `${id}: statement is empty`);\n\tif (claim.severity !== \"required\" && claim.severity !== \"advisory\") {\n\t\tthrow new ClaimGraphError(\"invalid_claim\", `${id}: severity must be required or advisory`);\n\t}\n\tif (claim.satisfaction.rule !== \"all\" && claim.satisfaction.rule !== \"any\") {\n\t\tthrow new ClaimGraphError(\"invalid_claim\", `${id}: satisfaction rule must be all or any`);\n\t}\n\tif (typeof OBSERVATION_TRUST_RANK[claim.trustFloor] !== \"number\") {\n\t\tthrow new ClaimGraphError(\"invalid_claim\", `${id}: unknown trustFloor ${String(claim.trustFloor)}`);\n\t}\n\tconst witnesses = claim.requiredWitnesses ?? 1;\n\tif (!Number.isInteger(witnesses) || witnesses < 1) {\n\t\tthrow new ClaimGraphError(\"invalid_claim\", `${id}: requiredWitnesses must be a positive integer`);\n\t}\n\tif (new Set(claim.satisfaction.inputs).size !== claim.satisfaction.inputs.length) {\n\t\tthrow new ClaimGraphError(\"invalid_claim\", `${id}: inputs contain duplicates`);\n\t}\n\tif (claim.satisfaction.inputs.includes(id)) throw new ClaimGraphError(\"cycle\", `${id} lists itself as an input`);\n}\n\n/** Throws on the first structural defect; returns the claims indexed by id otherwise. */\nexport function validateClaimGraph(graph: ClaimGraph): ReadonlyMap<string, ClaimNode> {\n\tif (graph.schemaVersion !== CLAIM_GRAPH_SCHEMA_VERSION) {\n\t\tthrow new ClaimGraphError(\"invalid_schema\", `Unsupported claim graph schema ${String(graph.schemaVersion)}`);\n\t}\n\tconst byId = new Map<string, ClaimNode>();\n\tfor (const claim of graph.claims) {\n\t\tvalidateClaim(claim);\n\t\tif (byId.has(claim.claimId)) throw new ClaimGraphError(\"duplicate_claim\", `Duplicate claim ${claim.claimId}`);\n\t\tbyId.set(claim.claimId, claim);\n\t}\n\tfor (const claim of graph.claims) {\n\t\tfor (const input of claim.satisfaction.inputs) {\n\t\t\tif (!byId.has(input))\n\t\t\t\tthrow new ClaimGraphError(\"unknown_input\", `${claim.claimId} references unknown ${input}`);\n\t\t}\n\t}\n\ttopologicalClaimOrder(graph, byId);\n\treturn byId;\n}\n\n/**\n * Children before parents, ties broken by claim id, so evaluation order is a\n * function of the graph alone. Kahn's algorithm; leftover nodes form a cycle.\n */\nexport function topologicalClaimOrder(graph: ClaimGraph, index?: ReadonlyMap<string, ClaimNode>): readonly ClaimNode[] {\n\tconst byId = index ?? new Map(graph.claims.map((claim) => [claim.claimId, claim]));\n\tconst remainingInputs = new Map<string, number>();\n\tconst dependents = new Map<string, string[]>();\n\tfor (const claim of graph.claims) {\n\t\tremainingInputs.set(claim.claimId, claim.satisfaction.inputs.length);\n\t\tfor (const input of claim.satisfaction.inputs) {\n\t\t\tconst list = dependents.get(input) ?? [];\n\t\t\tlist.push(claim.claimId);\n\t\t\tdependents.set(input, list);\n\t\t}\n\t}\n\tconst ready = graph.claims\n\t\t.filter((claim) => claim.satisfaction.inputs.length === 0)\n\t\t.map((claim) => claim.claimId)\n\t\t.sort(compareCodeUnits);\n\tconst ordered: ClaimNode[] = [];\n\twhile (ready.length > 0) {\n\t\tconst id = ready.shift() as string;\n\t\tconst claim = byId.get(id);\n\t\tif (claim !== undefined) ordered.push(claim);\n\t\tfor (const dependent of (dependents.get(id) ?? []).sort(compareCodeUnits)) {\n\t\t\tconst remaining = (remainingInputs.get(dependent) ?? 0) - 1;\n\t\t\tremainingInputs.set(dependent, remaining);\n\t\t\tif (remaining === 0) insertSorted(ready, dependent);\n\t\t}\n\t}\n\tif (ordered.length !== graph.claims.length) {\n\t\tconst stuck = graph.claims\n\t\t\t.map((claim) => claim.claimId)\n\t\t\t.filter((id) => !ordered.some((claim) => claim.claimId === id));\n\t\tthrow new ClaimGraphError(\n\t\t\t\"cycle\",\n\t\t\t`Claim graph contains a cycle through ${stuck.sort(compareCodeUnits).join(\", \")}`,\n\t\t);\n\t}\n\treturn ordered;\n}\n\nfunction insertSorted(list: string[], value: string): void {\n\tlet index = 0;\n\twhile (index < list.length && compareCodeUnits(list[index], value) < 0) index++;\n\tlist.splice(index, 0, value);\n}\n\n/** Claims that are nobody's input: the roots a public verdict is reported against. */\nexport function rootClaimIds(graph: ClaimGraph): readonly string[] {\n\tconst referenced = new Set(graph.claims.flatMap((claim) => claim.satisfaction.inputs));\n\treturn graph.claims\n\t\t.map((claim) => claim.claimId)\n\t\t.filter((id) => !referenced.has(id))\n\t\t.sort(compareCodeUnits);\n}\n\n/** Normalized graph for digesting: deterministic member order regardless of input order. */\nexport function canonicalClaimGraph(graph: ClaimGraph): ClaimGraph {\n\tconst claims = [...graph.claims]\n\t\t.sort((left, right) => compareCodeUnits(left.claimId, right.claimId))\n\t\t.map((claim) => ({\n\t\t\tclaimId: claim.claimId,\n\t\t\tkind: claim.kind,\n\t\t\tstatement: claim.statement,\n\t\t\tseverity: claim.severity,\n\t\t\tsatisfaction: { rule: claim.satisfaction.rule, inputs: [...claim.satisfaction.inputs].sort(compareCodeUnits) },\n\t\t\ttrustFloor: claim.trustFloor,\n\t\t\trequiredWitnesses: claim.requiredWitnesses ?? 1,\n\t\t\tscopeSensitive: claim.scopeSensitive ?? false,\n\t\t\tinvalidationKeys: [...claim.invalidationKeys].sort(compareCodeUnits),\n\t\t}));\n\treturn { schemaVersion: CLAIM_GRAPH_SCHEMA_VERSION, claims };\n}\n"]}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Structural rules for a claim graph: identity, references, acyclicity, and
|
|
3
|
+
* a deterministic evaluation order.
|
|
4
|
+
*
|
|
5
|
+
* Validation throws `ClaimGraphError` because a malformed graph is a
|
|
6
|
+
* programming or configuration defect, not an evaluation outcome; a proof
|
|
7
|
+
* must never report `inconclusive` for a graph it could not even read.
|
|
8
|
+
*
|
|
9
|
+
* `canonicalClaimGraph` returns the graph in a normalized shape (claims by
|
|
10
|
+
* id, inputs and keys sorted) so that any layer with a hasher can derive the
|
|
11
|
+
* graph digest the plan binds into operations, receipts, and snapshots.
|
|
12
|
+
* This package deliberately owns the normalization and not the hash.
|
|
13
|
+
*/
|
|
14
|
+
import { CLAIM_GRAPH_SCHEMA_VERSION, OBSERVATION_TRUST_RANK } from "./claim-types.js";
|
|
15
|
+
export class ClaimGraphError extends Error {
|
|
16
|
+
code;
|
|
17
|
+
constructor(code, message) {
|
|
18
|
+
super(message);
|
|
19
|
+
this.name = "ClaimGraphError";
|
|
20
|
+
this.code = code;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function compareCodeUnits(left, right) {
|
|
24
|
+
if (left === right)
|
|
25
|
+
return 0;
|
|
26
|
+
return left < right ? -1 : 1;
|
|
27
|
+
}
|
|
28
|
+
function isNonEmptyString(value) {
|
|
29
|
+
return typeof value === "string" && value.trim().length > 0;
|
|
30
|
+
}
|
|
31
|
+
function validateClaim(claim) {
|
|
32
|
+
const id = claim.claimId;
|
|
33
|
+
if (!isNonEmptyString(id))
|
|
34
|
+
throw new ClaimGraphError("invalid_claim", "claimId must be a non-empty string");
|
|
35
|
+
if (!isNonEmptyString(claim.statement))
|
|
36
|
+
throw new ClaimGraphError("invalid_claim", `${id}: statement is empty`);
|
|
37
|
+
if (claim.severity !== "required" && claim.severity !== "advisory") {
|
|
38
|
+
throw new ClaimGraphError("invalid_claim", `${id}: severity must be required or advisory`);
|
|
39
|
+
}
|
|
40
|
+
if (claim.satisfaction.rule !== "all" && claim.satisfaction.rule !== "any") {
|
|
41
|
+
throw new ClaimGraphError("invalid_claim", `${id}: satisfaction rule must be all or any`);
|
|
42
|
+
}
|
|
43
|
+
if (typeof OBSERVATION_TRUST_RANK[claim.trustFloor] !== "number") {
|
|
44
|
+
throw new ClaimGraphError("invalid_claim", `${id}: unknown trustFloor ${String(claim.trustFloor)}`);
|
|
45
|
+
}
|
|
46
|
+
const witnesses = claim.requiredWitnesses ?? 1;
|
|
47
|
+
if (!Number.isInteger(witnesses) || witnesses < 1) {
|
|
48
|
+
throw new ClaimGraphError("invalid_claim", `${id}: requiredWitnesses must be a positive integer`);
|
|
49
|
+
}
|
|
50
|
+
if (new Set(claim.satisfaction.inputs).size !== claim.satisfaction.inputs.length) {
|
|
51
|
+
throw new ClaimGraphError("invalid_claim", `${id}: inputs contain duplicates`);
|
|
52
|
+
}
|
|
53
|
+
if (claim.satisfaction.inputs.includes(id))
|
|
54
|
+
throw new ClaimGraphError("cycle", `${id} lists itself as an input`);
|
|
55
|
+
}
|
|
56
|
+
/** Throws on the first structural defect; returns the claims indexed by id otherwise. */
|
|
57
|
+
export function validateClaimGraph(graph) {
|
|
58
|
+
if (graph.schemaVersion !== CLAIM_GRAPH_SCHEMA_VERSION) {
|
|
59
|
+
throw new ClaimGraphError("invalid_schema", `Unsupported claim graph schema ${String(graph.schemaVersion)}`);
|
|
60
|
+
}
|
|
61
|
+
const byId = new Map();
|
|
62
|
+
for (const claim of graph.claims) {
|
|
63
|
+
validateClaim(claim);
|
|
64
|
+
if (byId.has(claim.claimId))
|
|
65
|
+
throw new ClaimGraphError("duplicate_claim", `Duplicate claim ${claim.claimId}`);
|
|
66
|
+
byId.set(claim.claimId, claim);
|
|
67
|
+
}
|
|
68
|
+
for (const claim of graph.claims) {
|
|
69
|
+
for (const input of claim.satisfaction.inputs) {
|
|
70
|
+
if (!byId.has(input))
|
|
71
|
+
throw new ClaimGraphError("unknown_input", `${claim.claimId} references unknown ${input}`);
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
topologicalClaimOrder(graph, byId);
|
|
75
|
+
return byId;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Children before parents, ties broken by claim id, so evaluation order is a
|
|
79
|
+
* function of the graph alone. Kahn's algorithm; leftover nodes form a cycle.
|
|
80
|
+
*/
|
|
81
|
+
export function topologicalClaimOrder(graph, index) {
|
|
82
|
+
const byId = index ?? new Map(graph.claims.map((claim) => [claim.claimId, claim]));
|
|
83
|
+
const remainingInputs = new Map();
|
|
84
|
+
const dependents = new Map();
|
|
85
|
+
for (const claim of graph.claims) {
|
|
86
|
+
remainingInputs.set(claim.claimId, claim.satisfaction.inputs.length);
|
|
87
|
+
for (const input of claim.satisfaction.inputs) {
|
|
88
|
+
const list = dependents.get(input) ?? [];
|
|
89
|
+
list.push(claim.claimId);
|
|
90
|
+
dependents.set(input, list);
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
const ready = graph.claims
|
|
94
|
+
.filter((claim) => claim.satisfaction.inputs.length === 0)
|
|
95
|
+
.map((claim) => claim.claimId)
|
|
96
|
+
.sort(compareCodeUnits);
|
|
97
|
+
const ordered = [];
|
|
98
|
+
while (ready.length > 0) {
|
|
99
|
+
const id = ready.shift();
|
|
100
|
+
const claim = byId.get(id);
|
|
101
|
+
if (claim !== undefined)
|
|
102
|
+
ordered.push(claim);
|
|
103
|
+
for (const dependent of (dependents.get(id) ?? []).sort(compareCodeUnits)) {
|
|
104
|
+
const remaining = (remainingInputs.get(dependent) ?? 0) - 1;
|
|
105
|
+
remainingInputs.set(dependent, remaining);
|
|
106
|
+
if (remaining === 0)
|
|
107
|
+
insertSorted(ready, dependent);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
if (ordered.length !== graph.claims.length) {
|
|
111
|
+
const stuck = graph.claims
|
|
112
|
+
.map((claim) => claim.claimId)
|
|
113
|
+
.filter((id) => !ordered.some((claim) => claim.claimId === id));
|
|
114
|
+
throw new ClaimGraphError("cycle", `Claim graph contains a cycle through ${stuck.sort(compareCodeUnits).join(", ")}`);
|
|
115
|
+
}
|
|
116
|
+
return ordered;
|
|
117
|
+
}
|
|
118
|
+
function insertSorted(list, value) {
|
|
119
|
+
let index = 0;
|
|
120
|
+
while (index < list.length && compareCodeUnits(list[index], value) < 0)
|
|
121
|
+
index++;
|
|
122
|
+
list.splice(index, 0, value);
|
|
123
|
+
}
|
|
124
|
+
/** Claims that are nobody's input: the roots a public verdict is reported against. */
|
|
125
|
+
export function rootClaimIds(graph) {
|
|
126
|
+
const referenced = new Set(graph.claims.flatMap((claim) => claim.satisfaction.inputs));
|
|
127
|
+
return graph.claims
|
|
128
|
+
.map((claim) => claim.claimId)
|
|
129
|
+
.filter((id) => !referenced.has(id))
|
|
130
|
+
.sort(compareCodeUnits);
|
|
131
|
+
}
|
|
132
|
+
/** Normalized graph for digesting: deterministic member order regardless of input order. */
|
|
133
|
+
export function canonicalClaimGraph(graph) {
|
|
134
|
+
const claims = [...graph.claims]
|
|
135
|
+
.sort((left, right) => compareCodeUnits(left.claimId, right.claimId))
|
|
136
|
+
.map((claim) => ({
|
|
137
|
+
claimId: claim.claimId,
|
|
138
|
+
kind: claim.kind,
|
|
139
|
+
statement: claim.statement,
|
|
140
|
+
severity: claim.severity,
|
|
141
|
+
satisfaction: { rule: claim.satisfaction.rule, inputs: [...claim.satisfaction.inputs].sort(compareCodeUnits) },
|
|
142
|
+
trustFloor: claim.trustFloor,
|
|
143
|
+
requiredWitnesses: claim.requiredWitnesses ?? 1,
|
|
144
|
+
scopeSensitive: claim.scopeSensitive ?? false,
|
|
145
|
+
invalidationKeys: [...claim.invalidationKeys].sort(compareCodeUnits),
|
|
146
|
+
}));
|
|
147
|
+
return { schemaVersion: CLAIM_GRAPH_SCHEMA_VERSION, claims };
|
|
148
|
+
}
|
|
149
|
+
//# sourceMappingURL=claim-graph.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claim-graph.js","sourceRoot":"","sources":["../../src/claims/claim-graph.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,EAAE,0BAA0B,EAAmC,sBAAsB,EAAE,MAAM,kBAAkB,CAAC;AAEvH,MAAM,OAAO,eAAgB,SAAQ,KAAK;IACzB,IAAI,CAMD;IAEnB,YAAY,IAA6B,EAAE,OAAe,EAAE;QAC3D,KAAK,CAAC,OAAO,CAAC,CAAC;QACf,IAAI,CAAC,IAAI,GAAG,iBAAiB,CAAC;QAC9B,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;IAAA,CACjB;CACD;AAED,SAAS,gBAAgB,CAAC,IAAY,EAAE,KAAa,EAAU;IAC9D,IAAI,IAAI,KAAK,KAAK;QAAE,OAAO,CAAC,CAAC;IAC7B,OAAO,IAAI,GAAG,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AAAA,CAC7B;AAED,SAAS,gBAAgB,CAAC,KAAc,EAAmB;IAC1D,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,GAAG,CAAC,CAAC;AAAA,CAC5D;AAED,SAAS,aAAa,CAAC,KAAgB,EAAQ;IAC9C,MAAM,EAAE,GAAG,KAAK,CAAC,OAAO,CAAC;IACzB,IAAI,CAAC,gBAAgB,CAAC,EAAE,CAAC;QAAE,MAAM,IAAI,eAAe,CAAC,eAAe,EAAE,oCAAoC,CAAC,CAAC;IAC5G,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,SAAS,CAAC;QAAE,MAAM,IAAI,eAAe,CAAC,eAAe,EAAE,GAAG,EAAE,sBAAsB,CAAC,CAAC;IAChH,IAAI,KAAK,CAAC,QAAQ,KAAK,UAAU,IAAI,KAAK,CAAC,QAAQ,KAAK,UAAU,EAAE,CAAC;QACpE,MAAM,IAAI,eAAe,CAAC,eAAe,EAAE,GAAG,EAAE,yCAAyC,CAAC,CAAC;IAC5F,CAAC;IACD,IAAI,KAAK,CAAC,YAAY,CAAC,IAAI,KAAK,KAAK,IAAI,KAAK,CAAC,YAAY,CAAC,IAAI,KAAK,KAAK,EAAE,CAAC;QAC5E,MAAM,IAAI,eAAe,CAAC,eAAe,EAAE,GAAG,EAAE,wCAAwC,CAAC,CAAC;IAC3F,CAAC;IACD,IAAI,OAAO,sBAAsB,CAAC,KAAK,CAAC,UAAU,CAAC,KAAK,QAAQ,EAAE,CAAC;QAClE,MAAM,IAAI,eAAe,CAAC,eAAe,EAAE,GAAG,EAAE,wBAAwB,MAAM,CAAC,KAAK,CAAC,UAAU,CAAC,EAAE,CAAC,CAAC;IACrG,CAAC;IACD,MAAM,SAAS,GAAG,KAAK,CAAC,iBAAiB,IAAI,CAAC,CAAC;IAC/C,IAAI,CAAC,MAAM,CAAC,SAAS,CAAC,SAAS,CAAC,IAAI,SAAS,GAAG,CAAC,EAAE,CAAC;QACnD,MAAM,IAAI,eAAe,CAAC,eAAe,EAAE,GAAG,EAAE,gDAAgD,CAAC,CAAC;IACnG,CAAC;IACD,IAAI,IAAI,GAAG,CAAC,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,IAAI,KAAK,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;QAClF,MAAM,IAAI,eAAe,CAAC,eAAe,EAAE,GAAG,EAAE,6BAA6B,CAAC,CAAC;IAChF,CAAC;IACD,IAAI,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC,QAAQ,CAAC,EAAE,CAAC;QAAE,MAAM,IAAI,eAAe,CAAC,OAAO,EAAE,GAAG,EAAE,2BAA2B,CAAC,CAAC;AAAA,CACjH;AAED,yFAAyF;AACzF,MAAM,UAAU,kBAAkB,CAAC,KAAiB,EAAkC;IACrF,IAAI,KAAK,CAAC,aAAa,KAAK,0BAA0B,EAAE,CAAC;QACxD,MAAM,IAAI,eAAe,CAAC,gBAAgB,EAAE,kCAAkC,MAAM,CAAC,KAAK,CAAC,aAAa,CAAC,EAAE,CAAC,CAAC;IAC9G,CAAC;IACD,MAAM,IAAI,GAAG,IAAI,GAAG,EAAqB,CAAC;IAC1C,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;QAClC,aAAa,CAAC,KAAK,CAAC,CAAC;QACrB,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC;YAAE,MAAM,IAAI,eAAe,CAAC,iBAAiB,EAAE,mBAAmB,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QAC9G,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IAChC,CAAC;IACD,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;QAClC,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC;YAC/C,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC;gBACnB,MAAM,IAAI,eAAe,CAAC,eAAe,EAAE,GAAG,KAAK,CAAC,OAAO,uBAAuB,KAAK,EAAE,CAAC,CAAC;QAC7F,CAAC;IACF,CAAC;IACD,qBAAqB,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;IACnC,OAAO,IAAI,CAAC;AAAA,CACZ;AAED;;;GAGG;AACH,MAAM,UAAU,qBAAqB,CAAC,KAAiB,EAAE,KAAsC,EAAwB;IACtH,MAAM,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC,CAAC,CAAC;IACnF,MAAM,eAAe,GAAG,IAAI,GAAG,EAAkB,CAAC;IAClD,MAAM,UAAU,GAAG,IAAI,GAAG,EAAoB,CAAC;IAC/C,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;QAClC,eAAe,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,EAAE,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;QACrE,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,YAAY,CAAC,MAAM,EAAE,CAAC;YAC/C,MAAM,IAAI,GAAG,UAAU,CAAC,GAAG,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;YACzC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;YACzB,UAAU,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,CAAC;QAC7B,CAAC;IACF,CAAC;IACD,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM;SACxB,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC;SACzD,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC;SAC7B,IAAI,CAAC,gBAAgB,CAAC,CAAC;IACzB,MAAM,OAAO,GAAgB,EAAE,CAAC;IAChC,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACzB,MAAM,EAAE,GAAG,KAAK,CAAC,KAAK,EAAY,CAAC;QACnC,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;QAC3B,IAAI,KAAK,KAAK,SAAS;YAAE,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC7C,KAAK,MAAM,SAAS,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,EAAE,CAAC;YAC3E,MAAM,SAAS,GAAG,CAAC,eAAe,CAAC,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;YAC5D,eAAe,CAAC,GAAG,CAAC,SAAS,EAAE,SAAS,CAAC,CAAC;YAC1C,IAAI,SAAS,KAAK,CAAC;gBAAE,YAAY,CAAC,KAAK,EAAE,SAAS,CAAC,CAAC;QACrD,CAAC;IACF,CAAC;IACD,IAAI,OAAO,CAAC,MAAM,KAAK,KAAK,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;QAC5C,MAAM,KAAK,GAAG,KAAK,CAAC,MAAM;aACxB,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC;aAC7B,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,KAAK,EAAE,CAAC,CAAC,CAAC;QACjE,MAAM,IAAI,eAAe,CACxB,OAAO,EACP,wCAAwC,KAAK,CAAC,IAAI,CAAC,gBAAgB,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACjF,CAAC;IACH,CAAC;IACD,OAAO,OAAO,CAAC;AAAA,CACf;AAED,SAAS,YAAY,CAAC,IAAc,EAAE,KAAa,EAAQ;IAC1D,IAAI,KAAK,GAAG,CAAC,CAAC;IACd,OAAO,KAAK,GAAG,IAAI,CAAC,MAAM,IAAI,gBAAgB,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE,KAAK,CAAC,GAAG,CAAC;QAAE,KAAK,EAAE,CAAC;IAChF,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,EAAE,KAAK,CAAC,CAAC;AAAA,CAC7B;AAED,sFAAsF;AACtF,MAAM,UAAU,YAAY,CAAC,KAAiB,EAAqB;IAClE,MAAM,UAAU,GAAG,IAAI,GAAG,CAAC,KAAK,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,CAAC;IACvF,OAAO,KAAK,CAAC,MAAM;SACjB,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC;SAC7B,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;SACnC,IAAI,CAAC,gBAAgB,CAAC,CAAC;AAAA,CACzB;AAED,4FAA4F;AAC5F,MAAM,UAAU,mBAAmB,CAAC,KAAiB,EAAc;IAClE,MAAM,MAAM,GAAG,CAAC,GAAG,KAAK,CAAC,MAAM,CAAC;SAC9B,IAAI,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,gBAAgB,CAAC,IAAI,CAAC,OAAO,EAAE,KAAK,CAAC,OAAO,CAAC,CAAC;SACpE,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QAChB,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,SAAS,EAAE,KAAK,CAAC,SAAS;QAC1B,QAAQ,EAAE,KAAK,CAAC,QAAQ;QACxB,YAAY,EAAE,EAAE,IAAI,EAAE,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,CAAC,GAAG,KAAK,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC,EAAE;QAC9G,UAAU,EAAE,KAAK,CAAC,UAAU;QAC5B,iBAAiB,EAAE,KAAK,CAAC,iBAAiB,IAAI,CAAC;QAC/C,cAAc,EAAE,KAAK,CAAC,cAAc,IAAI,KAAK;QAC7C,gBAAgB,EAAE,CAAC,GAAG,KAAK,CAAC,gBAAgB,CAAC,CAAC,IAAI,CAAC,gBAAgB,CAAC;KACpE,CAAC,CAAC,CAAC;IACL,OAAO,EAAE,aAAa,EAAE,0BAA0B,EAAE,MAAM,EAAE,CAAC;AAAA,CAC7D","sourcesContent":["/**\n * Structural rules for a claim graph: identity, references, acyclicity, and\n * a deterministic evaluation order.\n *\n * Validation throws `ClaimGraphError` because a malformed graph is a\n * programming or configuration defect, not an evaluation outcome; a proof\n * must never report `inconclusive` for a graph it could not even read.\n *\n * `canonicalClaimGraph` returns the graph in a normalized shape (claims by\n * id, inputs and keys sorted) so that any layer with a hasher can derive the\n * graph digest the plan binds into operations, receipts, and snapshots.\n * This package deliberately owns the normalization and not the hash.\n */\n\nimport { CLAIM_GRAPH_SCHEMA_VERSION, type ClaimGraph, type ClaimNode, OBSERVATION_TRUST_RANK } from \"./claim-types.ts\";\n\nexport class ClaimGraphError extends Error {\n\tpublic readonly code:\n\t\t| \"invalid_schema\"\n\t\t| \"invalid_claim\"\n\t\t| \"duplicate_claim\"\n\t\t| \"unknown_input\"\n\t\t| \"cycle\"\n\t\t| \"invalid_input\";\n\n\tconstructor(code: ClaimGraphError[\"code\"], message: string) {\n\t\tsuper(message);\n\t\tthis.name = \"ClaimGraphError\";\n\t\tthis.code = code;\n\t}\n}\n\nfunction compareCodeUnits(left: string, right: string): number {\n\tif (left === right) return 0;\n\treturn left < right ? -1 : 1;\n}\n\nfunction isNonEmptyString(value: unknown): value is string {\n\treturn typeof value === \"string\" && value.trim().length > 0;\n}\n\nfunction validateClaim(claim: ClaimNode): void {\n\tconst id = claim.claimId;\n\tif (!isNonEmptyString(id)) throw new ClaimGraphError(\"invalid_claim\", \"claimId must be a non-empty string\");\n\tif (!isNonEmptyString(claim.statement)) throw new ClaimGraphError(\"invalid_claim\", `${id}: statement is empty`);\n\tif (claim.severity !== \"required\" && claim.severity !== \"advisory\") {\n\t\tthrow new ClaimGraphError(\"invalid_claim\", `${id}: severity must be required or advisory`);\n\t}\n\tif (claim.satisfaction.rule !== \"all\" && claim.satisfaction.rule !== \"any\") {\n\t\tthrow new ClaimGraphError(\"invalid_claim\", `${id}: satisfaction rule must be all or any`);\n\t}\n\tif (typeof OBSERVATION_TRUST_RANK[claim.trustFloor] !== \"number\") {\n\t\tthrow new ClaimGraphError(\"invalid_claim\", `${id}: unknown trustFloor ${String(claim.trustFloor)}`);\n\t}\n\tconst witnesses = claim.requiredWitnesses ?? 1;\n\tif (!Number.isInteger(witnesses) || witnesses < 1) {\n\t\tthrow new ClaimGraphError(\"invalid_claim\", `${id}: requiredWitnesses must be a positive integer`);\n\t}\n\tif (new Set(claim.satisfaction.inputs).size !== claim.satisfaction.inputs.length) {\n\t\tthrow new ClaimGraphError(\"invalid_claim\", `${id}: inputs contain duplicates`);\n\t}\n\tif (claim.satisfaction.inputs.includes(id)) throw new ClaimGraphError(\"cycle\", `${id} lists itself as an input`);\n}\n\n/** Throws on the first structural defect; returns the claims indexed by id otherwise. */\nexport function validateClaimGraph(graph: ClaimGraph): ReadonlyMap<string, ClaimNode> {\n\tif (graph.schemaVersion !== CLAIM_GRAPH_SCHEMA_VERSION) {\n\t\tthrow new ClaimGraphError(\"invalid_schema\", `Unsupported claim graph schema ${String(graph.schemaVersion)}`);\n\t}\n\tconst byId = new Map<string, ClaimNode>();\n\tfor (const claim of graph.claims) {\n\t\tvalidateClaim(claim);\n\t\tif (byId.has(claim.claimId)) throw new ClaimGraphError(\"duplicate_claim\", `Duplicate claim ${claim.claimId}`);\n\t\tbyId.set(claim.claimId, claim);\n\t}\n\tfor (const claim of graph.claims) {\n\t\tfor (const input of claim.satisfaction.inputs) {\n\t\t\tif (!byId.has(input))\n\t\t\t\tthrow new ClaimGraphError(\"unknown_input\", `${claim.claimId} references unknown ${input}`);\n\t\t}\n\t}\n\ttopologicalClaimOrder(graph, byId);\n\treturn byId;\n}\n\n/**\n * Children before parents, ties broken by claim id, so evaluation order is a\n * function of the graph alone. Kahn's algorithm; leftover nodes form a cycle.\n */\nexport function topologicalClaimOrder(graph: ClaimGraph, index?: ReadonlyMap<string, ClaimNode>): readonly ClaimNode[] {\n\tconst byId = index ?? new Map(graph.claims.map((claim) => [claim.claimId, claim]));\n\tconst remainingInputs = new Map<string, number>();\n\tconst dependents = new Map<string, string[]>();\n\tfor (const claim of graph.claims) {\n\t\tremainingInputs.set(claim.claimId, claim.satisfaction.inputs.length);\n\t\tfor (const input of claim.satisfaction.inputs) {\n\t\t\tconst list = dependents.get(input) ?? [];\n\t\t\tlist.push(claim.claimId);\n\t\t\tdependents.set(input, list);\n\t\t}\n\t}\n\tconst ready = graph.claims\n\t\t.filter((claim) => claim.satisfaction.inputs.length === 0)\n\t\t.map((claim) => claim.claimId)\n\t\t.sort(compareCodeUnits);\n\tconst ordered: ClaimNode[] = [];\n\twhile (ready.length > 0) {\n\t\tconst id = ready.shift() as string;\n\t\tconst claim = byId.get(id);\n\t\tif (claim !== undefined) ordered.push(claim);\n\t\tfor (const dependent of (dependents.get(id) ?? []).sort(compareCodeUnits)) {\n\t\t\tconst remaining = (remainingInputs.get(dependent) ?? 0) - 1;\n\t\t\tremainingInputs.set(dependent, remaining);\n\t\t\tif (remaining === 0) insertSorted(ready, dependent);\n\t\t}\n\t}\n\tif (ordered.length !== graph.claims.length) {\n\t\tconst stuck = graph.claims\n\t\t\t.map((claim) => claim.claimId)\n\t\t\t.filter((id) => !ordered.some((claim) => claim.claimId === id));\n\t\tthrow new ClaimGraphError(\n\t\t\t\"cycle\",\n\t\t\t`Claim graph contains a cycle through ${stuck.sort(compareCodeUnits).join(\", \")}`,\n\t\t);\n\t}\n\treturn ordered;\n}\n\nfunction insertSorted(list: string[], value: string): void {\n\tlet index = 0;\n\twhile (index < list.length && compareCodeUnits(list[index], value) < 0) index++;\n\tlist.splice(index, 0, value);\n}\n\n/** Claims that are nobody's input: the roots a public verdict is reported against. */\nexport function rootClaimIds(graph: ClaimGraph): readonly string[] {\n\tconst referenced = new Set(graph.claims.flatMap((claim) => claim.satisfaction.inputs));\n\treturn graph.claims\n\t\t.map((claim) => claim.claimId)\n\t\t.filter((id) => !referenced.has(id))\n\t\t.sort(compareCodeUnits);\n}\n\n/** Normalized graph for digesting: deterministic member order regardless of input order. */\nexport function canonicalClaimGraph(graph: ClaimGraph): ClaimGraph {\n\tconst claims = [...graph.claims]\n\t\t.sort((left, right) => compareCodeUnits(left.claimId, right.claimId))\n\t\t.map((claim) => ({\n\t\t\tclaimId: claim.claimId,\n\t\t\tkind: claim.kind,\n\t\t\tstatement: claim.statement,\n\t\t\tseverity: claim.severity,\n\t\t\tsatisfaction: { rule: claim.satisfaction.rule, inputs: [...claim.satisfaction.inputs].sort(compareCodeUnits) },\n\t\t\ttrustFloor: claim.trustFloor,\n\t\t\trequiredWitnesses: claim.requiredWitnesses ?? 1,\n\t\t\tscopeSensitive: claim.scopeSensitive ?? false,\n\t\t\tinvalidationKeys: [...claim.invalidationKeys].sort(compareCodeUnits),\n\t\t}));\n\treturn { schemaVersion: CLAIM_GRAPH_SCHEMA_VERSION, claims };\n}\n"]}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claim Closure Graph V1 vocabulary.
|
|
3
|
+
*
|
|
4
|
+
* The flat `TaskSpec.claims` model answers "did this attempt satisfy its
|
|
5
|
+
* predicates?". The closure graph answers the stricter question a verified
|
|
6
|
+
* verdict needs: are all required claims closed by *qualified* witnesses —
|
|
7
|
+
* bound to this source root and environment, not expired, from a source the
|
|
8
|
+
* claim's policy trusts, independent enough to count separately — with no
|
|
9
|
+
* counterexample, no unresolved side effect, and a complete workspace scope?
|
|
10
|
+
*
|
|
11
|
+
* Pure vocabulary: no behaviour, no I/O, no clock. Evaluation lives in
|
|
12
|
+
* `claim-closure.ts`, structure checks in `claim-graph.ts`.
|
|
13
|
+
*/
|
|
14
|
+
export declare const CLAIM_GRAPH_SCHEMA_VERSION: "omk.claim-graph.v1";
|
|
15
|
+
export type ClaimNodeKind = "requirement" | "safety" | "scope" | "quality" | "performance" | "release";
|
|
16
|
+
export type ClaimSeverity = "required" | "advisory";
|
|
17
|
+
export type ClaimRule = "all" | "any";
|
|
18
|
+
/** Trust lattice, highest first. A claim's `trustFloor` names the weakest source that may witness it. */
|
|
19
|
+
export type ObservationSource = "deterministic_validator" | "trusted_attestation" | "workspace_witness" | "effect_reconciliation" | "independent_review" | "self_review" | "model_narrative";
|
|
20
|
+
export declare const OBSERVATION_TRUST_RANK: Readonly<Record<ObservationSource, number>>;
|
|
21
|
+
export type ObservationPolarity = "supports" | "violates";
|
|
22
|
+
export type ClaimVerdict = "satisfied" | "violated" | "missing" | "stale" | "incomplete_scope" | "insufficient_trust" | "waived";
|
|
23
|
+
/** Precedence when several verdicts compete; lower index wins. */
|
|
24
|
+
export declare const CLAIM_VERDICT_PRECEDENCE: readonly ClaimVerdict[];
|
|
25
|
+
export type WorkspaceCompleteness = "complete" | "partial_excluded" | "partial_truncated" | "unknown";
|
|
26
|
+
export type VerificationVerdict = "verified" | "unverified" | "inconclusive" | "violated";
|
|
27
|
+
export interface ClaimNode {
|
|
28
|
+
readonly claimId: string;
|
|
29
|
+
readonly kind: ClaimNodeKind;
|
|
30
|
+
readonly statement: string;
|
|
31
|
+
readonly severity: ClaimSeverity;
|
|
32
|
+
/** `inputs` are child claim ids. An empty list makes this a leaf witnessed by observations. */
|
|
33
|
+
readonly satisfaction: {
|
|
34
|
+
readonly rule: ClaimRule;
|
|
35
|
+
readonly inputs: readonly string[];
|
|
36
|
+
};
|
|
37
|
+
readonly trustFloor: ObservationSource;
|
|
38
|
+
/** Distinct independence groups a leaf needs among its qualified supporting witnesses. Default 1. */
|
|
39
|
+
readonly requiredWitnesses?: number;
|
|
40
|
+
/** Blocked with `incomplete_scope` whenever the workspace witness is not complete. */
|
|
41
|
+
readonly scopeSensitive?: boolean;
|
|
42
|
+
/** Keys a witness must have attested to; any missing key makes the witness stale for this claim. */
|
|
43
|
+
readonly invalidationKeys: readonly string[];
|
|
44
|
+
}
|
|
45
|
+
export interface ObservationNode {
|
|
46
|
+
readonly observationId: string;
|
|
47
|
+
readonly claimIds: readonly string[];
|
|
48
|
+
readonly polarity: ObservationPolarity;
|
|
49
|
+
readonly source: ObservationSource;
|
|
50
|
+
readonly receiptId?: string;
|
|
51
|
+
readonly sourceRoot: string;
|
|
52
|
+
readonly environmentDigest: string;
|
|
53
|
+
/** ISO-8601 instant after which the observation no longer qualifies. */
|
|
54
|
+
readonly validUntil?: string;
|
|
55
|
+
readonly invalidationKeys?: readonly string[];
|
|
56
|
+
/** Witnesses sharing a group count once; defaults to the observation id. */
|
|
57
|
+
readonly independenceGroup?: string;
|
|
58
|
+
}
|
|
59
|
+
export interface WaiverNode {
|
|
60
|
+
readonly waiverId: string;
|
|
61
|
+
readonly claimId: string;
|
|
62
|
+
readonly issuer: string;
|
|
63
|
+
readonly reason: string;
|
|
64
|
+
readonly sourceRoot: string;
|
|
65
|
+
readonly expiresAt: string;
|
|
66
|
+
}
|
|
67
|
+
export interface ClaimGraph {
|
|
68
|
+
readonly schemaVersion: typeof CLAIM_GRAPH_SCHEMA_VERSION;
|
|
69
|
+
readonly claims: readonly ClaimNode[];
|
|
70
|
+
}
|
|
71
|
+
export interface ProofClosureInput {
|
|
72
|
+
readonly graph: ClaimGraph;
|
|
73
|
+
readonly observations: readonly ObservationNode[];
|
|
74
|
+
readonly waivers: readonly WaiverNode[];
|
|
75
|
+
readonly sourceRoot: string;
|
|
76
|
+
readonly environmentDigest: string;
|
|
77
|
+
readonly workspaceCompleteness: WorkspaceCompleteness;
|
|
78
|
+
readonly unresolvedEffectIds: readonly string[];
|
|
79
|
+
/** ISO-8601 evaluation instant; injected so evaluation never reads a clock. */
|
|
80
|
+
readonly now: string;
|
|
81
|
+
}
|
|
82
|
+
export type ClaimReasonCode = "claim.satisfied" | "claim.violated" | "claim.missing" | "claim.stale" | "claim.incomplete_scope" | "claim.insufficient_trust" | "claim.waived";
|
|
83
|
+
export interface ClaimClosureEvaluation {
|
|
84
|
+
readonly claimId: string;
|
|
85
|
+
readonly severity: ClaimSeverity;
|
|
86
|
+
readonly verdict: ClaimVerdict;
|
|
87
|
+
readonly reasonCode: ClaimReasonCode;
|
|
88
|
+
/** Qualified observations that decided the verdict (violations, or counted witnesses). */
|
|
89
|
+
readonly observationIds: readonly string[];
|
|
90
|
+
readonly waiverId?: string;
|
|
91
|
+
}
|
|
92
|
+
export interface ProofClosureResult {
|
|
93
|
+
readonly verdict: VerificationVerdict;
|
|
94
|
+
readonly claimEvaluations: readonly ClaimClosureEvaluation[];
|
|
95
|
+
/** Required claims that are not closed, in evaluation order. */
|
|
96
|
+
readonly blockingClaimIds: readonly string[];
|
|
97
|
+
/** Smallest leaf set whose closure would unblock every blocking root; sorted. */
|
|
98
|
+
readonly minimalBlockingCut: readonly string[];
|
|
99
|
+
readonly unresolvedEffectIds: readonly string[];
|
|
100
|
+
readonly workspaceCompleteness: WorkspaceCompleteness;
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=claim-types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claim-types.d.ts","sourceRoot":"","sources":["../../src/claims/claim-types.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Claim Closure Graph V1 vocabulary.\n *\n * The flat `TaskSpec.claims` model answers \"did this attempt satisfy its\n * predicates?\". The closure graph answers the stricter question a verified\n * verdict needs: are all required claims closed by *qualified* witnesses —\n * bound to this source root and environment, not expired, from a source the\n * claim's policy trusts, independent enough to count separately — with no\n * counterexample, no unresolved side effect, and a complete workspace scope?\n *\n * Pure vocabulary: no behaviour, no I/O, no clock. Evaluation lives in\n * `claim-closure.ts`, structure checks in `claim-graph.ts`.\n */\n\nexport const CLAIM_GRAPH_SCHEMA_VERSION = \"omk.claim-graph.v1\" as const;\n\nexport type ClaimNodeKind = \"requirement\" | \"safety\" | \"scope\" | \"quality\" | \"performance\" | \"release\";\nexport type ClaimSeverity = \"required\" | \"advisory\";\nexport type ClaimRule = \"all\" | \"any\";\n\n/** Trust lattice, highest first. A claim's `trustFloor` names the weakest source that may witness it. */\nexport type ObservationSource =\n\t| \"deterministic_validator\"\n\t| \"trusted_attestation\"\n\t| \"workspace_witness\"\n\t| \"effect_reconciliation\"\n\t| \"independent_review\"\n\t| \"self_review\"\n\t| \"model_narrative\";\n\nexport const OBSERVATION_TRUST_RANK: Readonly<Record<ObservationSource, number>> = {\n\tdeterministic_validator: 6,\n\ttrusted_attestation: 5,\n\tworkspace_witness: 4,\n\teffect_reconciliation: 4,\n\tindependent_review: 3,\n\tself_review: 2,\n\tmodel_narrative: 1,\n};\n\nexport type ObservationPolarity = \"supports\" | \"violates\";\n\nexport type ClaimVerdict =\n\t| \"satisfied\"\n\t| \"violated\"\n\t| \"missing\"\n\t| \"stale\"\n\t| \"incomplete_scope\"\n\t| \"insufficient_trust\"\n\t| \"waived\";\n\n/** Precedence when several verdicts compete; lower index wins. */\nexport const CLAIM_VERDICT_PRECEDENCE: readonly ClaimVerdict[] = [\n\t\"violated\",\n\t\"stale\",\n\t\"incomplete_scope\",\n\t\"insufficient_trust\",\n\t\"missing\",\n\t\"waived\",\n\t\"satisfied\",\n];\n\nexport type WorkspaceCompleteness = \"complete\" | \"partial_excluded\" | \"partial_truncated\" | \"unknown\";\n\nexport type VerificationVerdict = \"verified\" | \"unverified\" | \"inconclusive\" | \"violated\";\n\nexport interface ClaimNode {\n\treadonly claimId: string;\n\treadonly kind: ClaimNodeKind;\n\treadonly statement: string;\n\treadonly severity: ClaimSeverity;\n\t/** `inputs` are child claim ids. An empty list makes this a leaf witnessed by observations. */\n\treadonly satisfaction: { readonly rule: ClaimRule; readonly inputs: readonly string[] };\n\treadonly trustFloor: ObservationSource;\n\t/** Distinct independence groups a leaf needs among its qualified supporting witnesses. Default 1. */\n\treadonly requiredWitnesses?: number;\n\t/** Blocked with `incomplete_scope` whenever the workspace witness is not complete. */\n\treadonly scopeSensitive?: boolean;\n\t/** Keys a witness must have attested to; any missing key makes the witness stale for this claim. */\n\treadonly invalidationKeys: readonly string[];\n}\n\nexport interface ObservationNode {\n\treadonly observationId: string;\n\treadonly claimIds: readonly string[];\n\treadonly polarity: ObservationPolarity;\n\treadonly source: ObservationSource;\n\treadonly receiptId?: string;\n\treadonly sourceRoot: string;\n\treadonly environmentDigest: string;\n\t/** ISO-8601 instant after which the observation no longer qualifies. */\n\treadonly validUntil?: string;\n\treadonly invalidationKeys?: readonly string[];\n\t/** Witnesses sharing a group count once; defaults to the observation id. */\n\treadonly independenceGroup?: string;\n}\n\nexport interface WaiverNode {\n\treadonly waiverId: string;\n\treadonly claimId: string;\n\treadonly issuer: string;\n\treadonly reason: string;\n\treadonly sourceRoot: string;\n\treadonly expiresAt: string;\n}\n\nexport interface ClaimGraph {\n\treadonly schemaVersion: typeof CLAIM_GRAPH_SCHEMA_VERSION;\n\treadonly claims: readonly ClaimNode[];\n}\n\nexport interface ProofClosureInput {\n\treadonly graph: ClaimGraph;\n\treadonly observations: readonly ObservationNode[];\n\treadonly waivers: readonly WaiverNode[];\n\treadonly sourceRoot: string;\n\treadonly environmentDigest: string;\n\treadonly workspaceCompleteness: WorkspaceCompleteness;\n\treadonly unresolvedEffectIds: readonly string[];\n\t/** ISO-8601 evaluation instant; injected so evaluation never reads a clock. */\n\treadonly now: string;\n}\n\nexport type ClaimReasonCode =\n\t| \"claim.satisfied\"\n\t| \"claim.violated\"\n\t| \"claim.missing\"\n\t| \"claim.stale\"\n\t| \"claim.incomplete_scope\"\n\t| \"claim.insufficient_trust\"\n\t| \"claim.waived\";\n\nexport interface ClaimClosureEvaluation {\n\treadonly claimId: string;\n\treadonly severity: ClaimSeverity;\n\treadonly verdict: ClaimVerdict;\n\treadonly reasonCode: ClaimReasonCode;\n\t/** Qualified observations that decided the verdict (violations, or counted witnesses). */\n\treadonly observationIds: readonly string[];\n\treadonly waiverId?: string;\n}\n\nexport interface ProofClosureResult {\n\treadonly verdict: VerificationVerdict;\n\treadonly claimEvaluations: readonly ClaimClosureEvaluation[];\n\t/** Required claims that are not closed, in evaluation order. */\n\treadonly blockingClaimIds: readonly string[];\n\t/** Smallest leaf set whose closure would unblock every blocking root; sorted. */\n\treadonly minimalBlockingCut: readonly string[];\n\treadonly unresolvedEffectIds: readonly string[];\n\treadonly workspaceCompleteness: WorkspaceCompleteness;\n}\n"]}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claim Closure Graph V1 vocabulary.
|
|
3
|
+
*
|
|
4
|
+
* The flat `TaskSpec.claims` model answers "did this attempt satisfy its
|
|
5
|
+
* predicates?". The closure graph answers the stricter question a verified
|
|
6
|
+
* verdict needs: are all required claims closed by *qualified* witnesses —
|
|
7
|
+
* bound to this source root and environment, not expired, from a source the
|
|
8
|
+
* claim's policy trusts, independent enough to count separately — with no
|
|
9
|
+
* counterexample, no unresolved side effect, and a complete workspace scope?
|
|
10
|
+
*
|
|
11
|
+
* Pure vocabulary: no behaviour, no I/O, no clock. Evaluation lives in
|
|
12
|
+
* `claim-closure.ts`, structure checks in `claim-graph.ts`.
|
|
13
|
+
*/
|
|
14
|
+
export const CLAIM_GRAPH_SCHEMA_VERSION = "omk.claim-graph.v1";
|
|
15
|
+
export const OBSERVATION_TRUST_RANK = {
|
|
16
|
+
deterministic_validator: 6,
|
|
17
|
+
trusted_attestation: 5,
|
|
18
|
+
workspace_witness: 4,
|
|
19
|
+
effect_reconciliation: 4,
|
|
20
|
+
independent_review: 3,
|
|
21
|
+
self_review: 2,
|
|
22
|
+
model_narrative: 1,
|
|
23
|
+
};
|
|
24
|
+
/** Precedence when several verdicts compete; lower index wins. */
|
|
25
|
+
export const CLAIM_VERDICT_PRECEDENCE = [
|
|
26
|
+
"violated",
|
|
27
|
+
"stale",
|
|
28
|
+
"incomplete_scope",
|
|
29
|
+
"insufficient_trust",
|
|
30
|
+
"missing",
|
|
31
|
+
"waived",
|
|
32
|
+
"satisfied",
|
|
33
|
+
];
|
|
34
|
+
//# sourceMappingURL=claim-types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"claim-types.js","sourceRoot":"","sources":["../../src/claims/claim-types.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,MAAM,CAAC,MAAM,0BAA0B,GAAG,oBAA6B,CAAC;AAgBxE,MAAM,CAAC,MAAM,sBAAsB,GAAgD;IAClF,uBAAuB,EAAE,CAAC;IAC1B,mBAAmB,EAAE,CAAC;IACtB,iBAAiB,EAAE,CAAC;IACpB,qBAAqB,EAAE,CAAC;IACxB,kBAAkB,EAAE,CAAC;IACrB,WAAW,EAAE,CAAC;IACd,eAAe,EAAE,CAAC;CAClB,CAAC;AAaF,kEAAkE;AAClE,MAAM,CAAC,MAAM,wBAAwB,GAA4B;IAChE,UAAU;IACV,OAAO;IACP,kBAAkB;IAClB,oBAAoB;IACpB,SAAS;IACT,QAAQ;IACR,WAAW;CACX,CAAC","sourcesContent":["/**\n * Claim Closure Graph V1 vocabulary.\n *\n * The flat `TaskSpec.claims` model answers \"did this attempt satisfy its\n * predicates?\". The closure graph answers the stricter question a verified\n * verdict needs: are all required claims closed by *qualified* witnesses —\n * bound to this source root and environment, not expired, from a source the\n * claim's policy trusts, independent enough to count separately — with no\n * counterexample, no unresolved side effect, and a complete workspace scope?\n *\n * Pure vocabulary: no behaviour, no I/O, no clock. Evaluation lives in\n * `claim-closure.ts`, structure checks in `claim-graph.ts`.\n */\n\nexport const CLAIM_GRAPH_SCHEMA_VERSION = \"omk.claim-graph.v1\" as const;\n\nexport type ClaimNodeKind = \"requirement\" | \"safety\" | \"scope\" | \"quality\" | \"performance\" | \"release\";\nexport type ClaimSeverity = \"required\" | \"advisory\";\nexport type ClaimRule = \"all\" | \"any\";\n\n/** Trust lattice, highest first. A claim's `trustFloor` names the weakest source that may witness it. */\nexport type ObservationSource =\n\t| \"deterministic_validator\"\n\t| \"trusted_attestation\"\n\t| \"workspace_witness\"\n\t| \"effect_reconciliation\"\n\t| \"independent_review\"\n\t| \"self_review\"\n\t| \"model_narrative\";\n\nexport const OBSERVATION_TRUST_RANK: Readonly<Record<ObservationSource, number>> = {\n\tdeterministic_validator: 6,\n\ttrusted_attestation: 5,\n\tworkspace_witness: 4,\n\teffect_reconciliation: 4,\n\tindependent_review: 3,\n\tself_review: 2,\n\tmodel_narrative: 1,\n};\n\nexport type ObservationPolarity = \"supports\" | \"violates\";\n\nexport type ClaimVerdict =\n\t| \"satisfied\"\n\t| \"violated\"\n\t| \"missing\"\n\t| \"stale\"\n\t| \"incomplete_scope\"\n\t| \"insufficient_trust\"\n\t| \"waived\";\n\n/** Precedence when several verdicts compete; lower index wins. */\nexport const CLAIM_VERDICT_PRECEDENCE: readonly ClaimVerdict[] = [\n\t\"violated\",\n\t\"stale\",\n\t\"incomplete_scope\",\n\t\"insufficient_trust\",\n\t\"missing\",\n\t\"waived\",\n\t\"satisfied\",\n];\n\nexport type WorkspaceCompleteness = \"complete\" | \"partial_excluded\" | \"partial_truncated\" | \"unknown\";\n\nexport type VerificationVerdict = \"verified\" | \"unverified\" | \"inconclusive\" | \"violated\";\n\nexport interface ClaimNode {\n\treadonly claimId: string;\n\treadonly kind: ClaimNodeKind;\n\treadonly statement: string;\n\treadonly severity: ClaimSeverity;\n\t/** `inputs` are child claim ids. An empty list makes this a leaf witnessed by observations. */\n\treadonly satisfaction: { readonly rule: ClaimRule; readonly inputs: readonly string[] };\n\treadonly trustFloor: ObservationSource;\n\t/** Distinct independence groups a leaf needs among its qualified supporting witnesses. Default 1. */\n\treadonly requiredWitnesses?: number;\n\t/** Blocked with `incomplete_scope` whenever the workspace witness is not complete. */\n\treadonly scopeSensitive?: boolean;\n\t/** Keys a witness must have attested to; any missing key makes the witness stale for this claim. */\n\treadonly invalidationKeys: readonly string[];\n}\n\nexport interface ObservationNode {\n\treadonly observationId: string;\n\treadonly claimIds: readonly string[];\n\treadonly polarity: ObservationPolarity;\n\treadonly source: ObservationSource;\n\treadonly receiptId?: string;\n\treadonly sourceRoot: string;\n\treadonly environmentDigest: string;\n\t/** ISO-8601 instant after which the observation no longer qualifies. */\n\treadonly validUntil?: string;\n\treadonly invalidationKeys?: readonly string[];\n\t/** Witnesses sharing a group count once; defaults to the observation id. */\n\treadonly independenceGroup?: string;\n}\n\nexport interface WaiverNode {\n\treadonly waiverId: string;\n\treadonly claimId: string;\n\treadonly issuer: string;\n\treadonly reason: string;\n\treadonly sourceRoot: string;\n\treadonly expiresAt: string;\n}\n\nexport interface ClaimGraph {\n\treadonly schemaVersion: typeof CLAIM_GRAPH_SCHEMA_VERSION;\n\treadonly claims: readonly ClaimNode[];\n}\n\nexport interface ProofClosureInput {\n\treadonly graph: ClaimGraph;\n\treadonly observations: readonly ObservationNode[];\n\treadonly waivers: readonly WaiverNode[];\n\treadonly sourceRoot: string;\n\treadonly environmentDigest: string;\n\treadonly workspaceCompleteness: WorkspaceCompleteness;\n\treadonly unresolvedEffectIds: readonly string[];\n\t/** ISO-8601 evaluation instant; injected so evaluation never reads a clock. */\n\treadonly now: string;\n}\n\nexport type ClaimReasonCode =\n\t| \"claim.satisfied\"\n\t| \"claim.violated\"\n\t| \"claim.missing\"\n\t| \"claim.stale\"\n\t| \"claim.incomplete_scope\"\n\t| \"claim.insufficient_trust\"\n\t| \"claim.waived\";\n\nexport interface ClaimClosureEvaluation {\n\treadonly claimId: string;\n\treadonly severity: ClaimSeverity;\n\treadonly verdict: ClaimVerdict;\n\treadonly reasonCode: ClaimReasonCode;\n\t/** Qualified observations that decided the verdict (violations, or counted witnesses). */\n\treadonly observationIds: readonly string[];\n\treadonly waiverId?: string;\n}\n\nexport interface ProofClosureResult {\n\treadonly verdict: VerificationVerdict;\n\treadonly claimEvaluations: readonly ClaimClosureEvaluation[];\n\t/** Required claims that are not closed, in evaluation order. */\n\treadonly blockingClaimIds: readonly string[];\n\t/** Smallest leaf set whose closure would unblock every blocking root; sorted. */\n\treadonly minimalBlockingCut: readonly string[];\n\treadonly unresolvedEffectIds: readonly string[];\n\treadonly workspaceCompleteness: WorkspaceCompleteness;\n}\n"]}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
export { isBlockingVerdict, minimalBlockingCut } from "./claims/claim-blocking-cut.ts";
|
|
2
|
+
export { evaluateProofClosure } from "./claims/claim-closure.ts";
|
|
3
|
+
export { ClaimGraphError, canonicalClaimGraph, rootClaimIds, topologicalClaimOrder, validateClaimGraph, } from "./claims/claim-graph.ts";
|
|
4
|
+
export { CLAIM_GRAPH_SCHEMA_VERSION, CLAIM_VERDICT_PRECEDENCE, type ClaimClosureEvaluation, type ClaimGraph, type ClaimNode, type ClaimNodeKind, type ClaimRule, type ClaimSeverity, type ClaimVerdict, OBSERVATION_TRUST_RANK, type ObservationNode, type ObservationPolarity, type ObservationSource, type ProofClosureInput, type ProofClosureResult, type VerificationVerdict, type WaiverNode, type WorkspaceCompleteness, } from "./claims/claim-types.ts";
|
|
1
5
|
export { reduceRuntimeDecision } from "./decision.ts";
|
|
2
6
|
export { evaluateTask, ProtocolInvariantError } from "./evaluation.ts";
|
|
3
7
|
export type { AllCondition, AnyCondition, AttemptExecutor, AttemptOutcome, AttemptTrigger, ClaimCondition, ClaimEvaluation, ClaimPredicate, ClaimReasonCode, ClaimResult, EvaluationInput, EvaluationResult, ExecutionAttempt, JsonObject, JsonPrimitive, JsonValue, NotCondition, Observation, ObservationCondition, ProtocolVersion, RequirementLevel, RuntimeAction, RuntimeDecision, RuntimeDecisionInput, RuntimeDecisionPolicy, RuntimeDecisionReason, SemanticVerdict, TaskSpec, WaiverRecord, } from "./types.ts";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AACvE,YAAY,EACX,YAAY,EACZ,YAAY,EACZ,eAAe,EACf,cAAc,EACd,cAAc,EACd,cAAc,EACd,eAAe,EACf,cAAc,EACd,eAAe,EACf,WAAW,EACX,eAAe,EACf,gBAAgB,EAChB,gBAAgB,EAChB,UAAU,EACV,aAAa,EACb,SAAS,EACT,YAAY,EACZ,WAAW,EACX,oBAAoB,EACpB,eAAe,EACf,gBAAgB,EAChB,aAAa,EACb,eAAe,EACf,oBAAoB,EACpB,qBAAqB,EACrB,qBAAqB,EACrB,eAAe,EACf,QAAQ,EACR,YAAY,GACZ,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EACN,uBAAuB,EACvB,qBAAqB,EACrB,qBAAqB,EACrB,gBAAgB,EAChB,oBAAoB,EACpB,aAAa,EACb,iBAAiB,GACjB,MAAM,iBAAiB,CAAC","sourcesContent":["export { reduceRuntimeDecision } from \"./decision.ts\";\nexport { evaluateTask, ProtocolInvariantError } from \"./evaluation.ts\";\nexport type {\n\tAllCondition,\n\tAnyCondition,\n\tAttemptExecutor,\n\tAttemptOutcome,\n\tAttemptTrigger,\n\tClaimCondition,\n\tClaimEvaluation,\n\tClaimPredicate,\n\tClaimReasonCode,\n\tClaimResult,\n\tEvaluationInput,\n\tEvaluationResult,\n\tExecutionAttempt,\n\tJsonObject,\n\tJsonPrimitive,\n\tJsonValue,\n\tNotCondition,\n\tObservation,\n\tObservationCondition,\n\tProtocolVersion,\n\tRequirementLevel,\n\tRuntimeAction,\n\tRuntimeDecision,\n\tRuntimeDecisionInput,\n\tRuntimeDecisionPolicy,\n\tRuntimeDecisionReason,\n\tSemanticVerdict,\n\tTaskSpec,\n\tWaiverRecord,\n} from \"./types.ts\";\nexport { PROTOCOL_VERSION } from \"./types.ts\";\nexport {\n\tProtocolValidationError,\n\tparseEvaluationResult,\n\tparseExecutionAttempt,\n\tparseObservation,\n\tparseRuntimeDecision,\n\tparseTaskSpec,\n\tparseWaiverRecord,\n} from \"./validation.ts\";\n"]}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACvF,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACjE,OAAO,EACN,eAAe,EACf,mBAAmB,EACnB,YAAY,EACZ,qBAAqB,EACrB,kBAAkB,GAClB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACN,0BAA0B,EAC1B,wBAAwB,EACxB,KAAK,sBAAsB,EAC3B,KAAK,UAAU,EACf,KAAK,SAAS,EACd,KAAK,aAAa,EAClB,KAAK,SAAS,EACd,KAAK,aAAa,EAClB,KAAK,YAAY,EACjB,sBAAsB,EACtB,KAAK,eAAe,EACpB,KAAK,mBAAmB,EACxB,KAAK,iBAAiB,EACtB,KAAK,iBAAiB,EACtB,KAAK,kBAAkB,EACvB,KAAK,mBAAmB,EACxB,KAAK,UAAU,EACf,KAAK,qBAAqB,GAC1B,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AACvE,YAAY,EACX,YAAY,EACZ,YAAY,EACZ,eAAe,EACf,cAAc,EACd,cAAc,EACd,cAAc,EACd,eAAe,EACf,cAAc,EACd,eAAe,EACf,WAAW,EACX,eAAe,EACf,gBAAgB,EAChB,gBAAgB,EAChB,UAAU,EACV,aAAa,EACb,SAAS,EACT,YAAY,EACZ,WAAW,EACX,oBAAoB,EACpB,eAAe,EACf,gBAAgB,EAChB,aAAa,EACb,eAAe,EACf,oBAAoB,EACpB,qBAAqB,EACrB,qBAAqB,EACrB,eAAe,EACf,QAAQ,EACR,YAAY,GACZ,MAAM,YAAY,CAAC;AACpB,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EACN,uBAAuB,EACvB,qBAAqB,EACrB,qBAAqB,EACrB,gBAAgB,EAChB,oBAAoB,EACpB,aAAa,EACb,iBAAiB,GACjB,MAAM,iBAAiB,CAAC","sourcesContent":["export { isBlockingVerdict, minimalBlockingCut } from \"./claims/claim-blocking-cut.ts\";\nexport { evaluateProofClosure } from \"./claims/claim-closure.ts\";\nexport {\n\tClaimGraphError,\n\tcanonicalClaimGraph,\n\trootClaimIds,\n\ttopologicalClaimOrder,\n\tvalidateClaimGraph,\n} from \"./claims/claim-graph.ts\";\nexport {\n\tCLAIM_GRAPH_SCHEMA_VERSION,\n\tCLAIM_VERDICT_PRECEDENCE,\n\ttype ClaimClosureEvaluation,\n\ttype ClaimGraph,\n\ttype ClaimNode,\n\ttype ClaimNodeKind,\n\ttype ClaimRule,\n\ttype ClaimSeverity,\n\ttype ClaimVerdict,\n\tOBSERVATION_TRUST_RANK,\n\ttype ObservationNode,\n\ttype ObservationPolarity,\n\ttype ObservationSource,\n\ttype ProofClosureInput,\n\ttype ProofClosureResult,\n\ttype VerificationVerdict,\n\ttype WaiverNode,\n\ttype WorkspaceCompleteness,\n} from \"./claims/claim-types.ts\";\nexport { reduceRuntimeDecision } from \"./decision.ts\";\nexport { evaluateTask, ProtocolInvariantError } from \"./evaluation.ts\";\nexport type {\n\tAllCondition,\n\tAnyCondition,\n\tAttemptExecutor,\n\tAttemptOutcome,\n\tAttemptTrigger,\n\tClaimCondition,\n\tClaimEvaluation,\n\tClaimPredicate,\n\tClaimReasonCode,\n\tClaimResult,\n\tEvaluationInput,\n\tEvaluationResult,\n\tExecutionAttempt,\n\tJsonObject,\n\tJsonPrimitive,\n\tJsonValue,\n\tNotCondition,\n\tObservation,\n\tObservationCondition,\n\tProtocolVersion,\n\tRequirementLevel,\n\tRuntimeAction,\n\tRuntimeDecision,\n\tRuntimeDecisionInput,\n\tRuntimeDecisionPolicy,\n\tRuntimeDecisionReason,\n\tSemanticVerdict,\n\tTaskSpec,\n\tWaiverRecord,\n} from \"./types.ts\";\nexport { PROTOCOL_VERSION } from \"./types.ts\";\nexport {\n\tProtocolValidationError,\n\tparseEvaluationResult,\n\tparseExecutionAttempt,\n\tparseObservation,\n\tparseRuntimeDecision,\n\tparseTaskSpec,\n\tparseWaiverRecord,\n} from \"./validation.ts\";\n"]}
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,7 @@
|
|
|
1
|
+
export { isBlockingVerdict, minimalBlockingCut } from "./claims/claim-blocking-cut.js";
|
|
2
|
+
export { evaluateProofClosure } from "./claims/claim-closure.js";
|
|
3
|
+
export { ClaimGraphError, canonicalClaimGraph, rootClaimIds, topologicalClaimOrder, validateClaimGraph, } from "./claims/claim-graph.js";
|
|
4
|
+
export { CLAIM_GRAPH_SCHEMA_VERSION, CLAIM_VERDICT_PRECEDENCE, OBSERVATION_TRUST_RANK, } from "./claims/claim-types.js";
|
|
1
5
|
export { reduceRuntimeDecision } from "./decision.js";
|
|
2
6
|
export { evaluateTask, ProtocolInvariantError } from "./evaluation.js";
|
|
3
7
|
export { PROTOCOL_VERSION } from "./types.js";
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAgCvE,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EACN,uBAAuB,EACvB,qBAAqB,EACrB,qBAAqB,EACrB,gBAAgB,EAChB,oBAAoB,EACpB,aAAa,EACb,iBAAiB,GACjB,MAAM,iBAAiB,CAAC","sourcesContent":["export { reduceRuntimeDecision } from \"./decision.ts\";\nexport { evaluateTask, ProtocolInvariantError } from \"./evaluation.ts\";\nexport type {\n\tAllCondition,\n\tAnyCondition,\n\tAttemptExecutor,\n\tAttemptOutcome,\n\tAttemptTrigger,\n\tClaimCondition,\n\tClaimEvaluation,\n\tClaimPredicate,\n\tClaimReasonCode,\n\tClaimResult,\n\tEvaluationInput,\n\tEvaluationResult,\n\tExecutionAttempt,\n\tJsonObject,\n\tJsonPrimitive,\n\tJsonValue,\n\tNotCondition,\n\tObservation,\n\tObservationCondition,\n\tProtocolVersion,\n\tRequirementLevel,\n\tRuntimeAction,\n\tRuntimeDecision,\n\tRuntimeDecisionInput,\n\tRuntimeDecisionPolicy,\n\tRuntimeDecisionReason,\n\tSemanticVerdict,\n\tTaskSpec,\n\tWaiverRecord,\n} from \"./types.ts\";\nexport { PROTOCOL_VERSION } from \"./types.ts\";\nexport {\n\tProtocolValidationError,\n\tparseEvaluationResult,\n\tparseExecutionAttempt,\n\tparseObservation,\n\tparseRuntimeDecision,\n\tparseTaskSpec,\n\tparseWaiverRecord,\n} from \"./validation.ts\";\n"]}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAC;AACvF,OAAO,EAAE,oBAAoB,EAAE,MAAM,2BAA2B,CAAC;AACjE,OAAO,EACN,eAAe,EACf,mBAAmB,EACnB,YAAY,EACZ,qBAAqB,EACrB,kBAAkB,GAClB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EACN,0BAA0B,EAC1B,wBAAwB,EAQxB,sBAAsB,GAStB,MAAM,yBAAyB,CAAC;AACjC,OAAO,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AACtD,OAAO,EAAE,YAAY,EAAE,sBAAsB,EAAE,MAAM,iBAAiB,CAAC;AAgCvE,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EACN,uBAAuB,EACvB,qBAAqB,EACrB,qBAAqB,EACrB,gBAAgB,EAChB,oBAAoB,EACpB,aAAa,EACb,iBAAiB,GACjB,MAAM,iBAAiB,CAAC","sourcesContent":["export { isBlockingVerdict, minimalBlockingCut } from \"./claims/claim-blocking-cut.ts\";\nexport { evaluateProofClosure } from \"./claims/claim-closure.ts\";\nexport {\n\tClaimGraphError,\n\tcanonicalClaimGraph,\n\trootClaimIds,\n\ttopologicalClaimOrder,\n\tvalidateClaimGraph,\n} from \"./claims/claim-graph.ts\";\nexport {\n\tCLAIM_GRAPH_SCHEMA_VERSION,\n\tCLAIM_VERDICT_PRECEDENCE,\n\ttype ClaimClosureEvaluation,\n\ttype ClaimGraph,\n\ttype ClaimNode,\n\ttype ClaimNodeKind,\n\ttype ClaimRule,\n\ttype ClaimSeverity,\n\ttype ClaimVerdict,\n\tOBSERVATION_TRUST_RANK,\n\ttype ObservationNode,\n\ttype ObservationPolarity,\n\ttype ObservationSource,\n\ttype ProofClosureInput,\n\ttype ProofClosureResult,\n\ttype VerificationVerdict,\n\ttype WaiverNode,\n\ttype WorkspaceCompleteness,\n} from \"./claims/claim-types.ts\";\nexport { reduceRuntimeDecision } from \"./decision.ts\";\nexport { evaluateTask, ProtocolInvariantError } from \"./evaluation.ts\";\nexport type {\n\tAllCondition,\n\tAnyCondition,\n\tAttemptExecutor,\n\tAttemptOutcome,\n\tAttemptTrigger,\n\tClaimCondition,\n\tClaimEvaluation,\n\tClaimPredicate,\n\tClaimReasonCode,\n\tClaimResult,\n\tEvaluationInput,\n\tEvaluationResult,\n\tExecutionAttempt,\n\tJsonObject,\n\tJsonPrimitive,\n\tJsonValue,\n\tNotCondition,\n\tObservation,\n\tObservationCondition,\n\tProtocolVersion,\n\tRequirementLevel,\n\tRuntimeAction,\n\tRuntimeDecision,\n\tRuntimeDecisionInput,\n\tRuntimeDecisionPolicy,\n\tRuntimeDecisionReason,\n\tSemanticVerdict,\n\tTaskSpec,\n\tWaiverRecord,\n} from \"./types.ts\";\nexport { PROTOCOL_VERSION } from \"./types.ts\";\nexport {\n\tProtocolValidationError,\n\tparseEvaluationResult,\n\tparseExecutionAttempt,\n\tparseObservation,\n\tparseRuntimeDecision,\n\tparseTaskSpec,\n\tparseWaiverRecord,\n} from \"./validation.ts\";\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "omk-protocol",
|
|
3
|
-
"version": "0.98.
|
|
3
|
+
"version": "0.98.3",
|
|
4
4
|
"description": "Canonical OMK Run Protocol contracts and pure semantic reducers",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -14,7 +14,8 @@
|
|
|
14
14
|
},
|
|
15
15
|
"files": [
|
|
16
16
|
"dist",
|
|
17
|
-
"README.md"
|
|
17
|
+
"README.md",
|
|
18
|
+
"CHANGELOG.md"
|
|
18
19
|
],
|
|
19
20
|
"scripts": {
|
|
20
21
|
"clean": "shx rm -rf dist",
|