omk-protocol 0.98.3 → 0.98.4

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 CHANGED
@@ -1,5 +1,17 @@
1
1
  # Changelog
2
2
 
3
+ ## [Unreleased]
4
+
5
+ ## [0.98.4] - 2026-09-10
6
+
7
+ ### Added
8
+
9
+ - Added bounded `explainBlockingCut()` metadata and opt-in `witnessIndependence: "explicit-groups"`. The compatibility default remains observation-ID grouping.
10
+
11
+ ### Fixed
12
+
13
+ - Shared DAG branches can share a repair instead of choosing incompatible local minima. Composite-local obligations remain visible. A bounded-search greedy fallback explicitly does not claim minimum cardinality.
14
+
3
15
  ## [0.98.3] - 2026-09-06
4
16
 
5
17
  ### Added
package/README.md CHANGED
@@ -21,6 +21,35 @@ trust floors, witness groups, expiry, workspace completeness and unresolved-effe
21
21
  boundaries; those supplied facts remain caller trust boundaries. It does not attest a
22
22
  runner or automatically decide an ordinary chat turn.
23
23
 
24
+ ## Claim closure and blocking explanations
25
+
26
+ `evaluateProofClosure()` evaluates qualified witnesses on an all/any claim DAG.
27
+ Set `witnessIndependence: "explicit-groups"` for a strict profile: multi-witness
28
+ claims (`requiredWitnesses > 1`) then require explicit, nonempty `independenceGroup`
29
+ values. Merely changing an observation ID does not create another independent
30
+ witness in that profile. Single-witness claims keep their existing behavior.
31
+
32
+ The compatibility default remains `legacy-observation-id`; the result reports the
33
+ resolved policy. Existing callers must opt into the strict profile deliberately.
34
+ Group identities still need binding to real evidence origins by a trusted caller;
35
+ the reducer cannot authenticate an arbitrary caller-supplied label.
36
+
37
+ `explainBlockingCut()` returns a bounded antichain repair explanation. Shared DAG
38
+ branches can share one repair. Composite-local counterexamples and scope obligations
39
+ are retained alongside child obligations rather than being hidden by them.
40
+
41
+ - `blockingCut.optimality: "minimum"` means minimum cardinality in this snapshot's
42
+ repair model, not semantic correctness after a real change.
43
+ - At the candidate/operation limit, `algorithm: "greedy"`, `truncated: true`, and
44
+ `optimality: "not-proven"` identify a fallback with no minimality guarantee.
45
+ - The legacy `minimalBlockingCut` array remains as a compatibility projection.
46
+ Consumers needing optimality must read the explanation metadata. Older results
47
+ without that metadata do not establish a minimum.
48
+
49
+ Repair explanations never close claims, reconcile effects, issue waivers, or authorize
50
+ merges. Re-evaluate evidence after a change. Set operations depend on candidate and
51
+ set sizes; the shared-DAG optimization is not a linear-time minimum-cut algorithm.
52
+
24
53
  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.
25
54
 
26
55
  See [OMK Run Protocol v1](https://github.com/dmae97/omk/blob/main/packages/coding-agent/docs/run-protocol.md) for evaluation rules, receipt bridging, migration status, and authority boundaries.
@@ -1,17 +1,10 @@
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";
1
+ import { type BlockingCutExplanation, type ClaimClosureEvaluation, type ClaimNode, type ClaimVerdict } from "./claim-types.ts";
2
+ export declare const MAX_BLOCKING_CUT_CANDIDATES = 128;
3
+ type Cut = readonly string[];
15
4
  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[];
5
+ /** Prefer this API over the legacy array-only projection when optimality matters. */
6
+ export declare function explainBlockingCut(claims: ReadonlyMap<string, ClaimNode>, evaluations: ReadonlyMap<string, ClaimClosureEvaluation>, rootIds: readonly string[]): BlockingCutExplanation;
7
+ /** Compatibility name: on a bounded-search fallback this array is not guaranteed minimal. */
8
+ export declare function minimalBlockingCut(claims: ReadonlyMap<string, ClaimNode>, evaluations: ReadonlyMap<string, ClaimClosureEvaluation>, rootIds: readonly string[]): Cut;
9
+ export {};
17
10
  //# sourceMappingURL=claim-blocking-cut.d.ts.map
@@ -1 +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"]}
1
+ {"version":3,"file":"claim-blocking-cut.d.ts","sourceRoot":"","sources":["../../src/claims/claim-blocking-cut.ts"],"names":[],"mappings":"AAQA,OAAO,EACN,KAAK,sBAAsB,EAE3B,KAAK,sBAAsB,EAC3B,KAAK,SAAS,EACd,KAAK,YAAY,EACjB,MAAM,kBAAkB,CAAC;AAS1B,eAAO,MAAM,2BAA2B,MAAM,CAAC;AAG/C,KAAK,GAAG,GAAG,SAAS,MAAM,EAAE,CAAC;AAc7B,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,YAAY,GAAG,OAAO,CAEhE;AAyID,qFAAqF;AACrF,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,sBAAsB,CA2BxB;AAED,6FAA6F;AAC7F,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,GAAG,CAEL","sourcesContent":["/**\n * Bounded blocking explanations for a validated all/any claim DAG.\n * Shared subgraphs require alternative repair sets, not a local cheapest branch.\n * Exact antichains can grow exponentially; bounds fall back to a deterministic,\n * not-proven explanation. Set-operation cost also depends on output-set size.\n * A repair is an explanatory obligation, never permission to satisfy a claim.\n */\nimport { ClaimGraphError, topologicalClaimOrder } from \"./claim-graph.ts\";\nimport {\n\ttype BlockingCutExplanation,\n\tCLAIM_GRAPH_SCHEMA_VERSION,\n\ttype ClaimClosureEvaluation,\n\ttype ClaimNode,\n\ttype ClaimVerdict,\n} 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];\nexport const MAX_BLOCKING_CUT_CANDIDATES = 128;\nconst MAX_CUT_OPERATIONS = 65536;\nclass CutSearchLimit extends Error {}\ntype Cut = readonly string[];\ntype Family = readonly Cut[];\ninterface CutNode {\n\treadonly id: string;\n\treadonly rule: \"all\" | \"any\";\n\treadonly inputs: readonly string[];\n\treadonly closed: boolean;\n\treadonly local: boolean;\n}\n/** Search-owned mutable accounting, including work before a limit is reached. */\ninterface CutSearchBudget {\n\toperations: number;\n}\n\nexport function isBlockingVerdict(verdict: ClaimVerdict): boolean {\n\treturn BLOCKING_VERDICTS.includes(verdict);\n}\nfunction compareIds(left: string, right: string): number {\n\tif (left === right) return 0;\n\treturn left < right ? -1 : 1;\n}\nfunction compareCuts(left: Cut, right: Cut): number {\n\tif (left.length !== right.length) return left.length - right.length;\n\tfor (let i = 0; i < left.length; i++) {\n\t\tconst order = compareIds(left[i], right[i]);\n\t\tif (order) return order;\n\t}\n\treturn 0;\n}\nfunction union(sets: readonly Cut[]): Cut {\n\treturn [...new Set(sets.flat())].sort(compareIds);\n}\nfunction* unions(left: Family, right: Family): Generator<Cut> {\n\tfor (const a of left) for (const b of right) yield union([a, b]);\n}\nfunction* alternatives(families: readonly Family[]): Generator<Cut> {\n\tfor (const family of families) yield* family;\n}\n\nfunction cutNodes(\n\tclaims: ReadonlyMap<string, ClaimNode>,\n\tevaluations: ReadonlyMap<string, ClaimClosureEvaluation>,\n): CutNode[] {\n\tconst ordered = topologicalClaimOrder(\n\t\t{ schemaVersion: CLAIM_GRAPH_SCHEMA_VERSION, claims: [...claims.values()] },\n\t\tclaims,\n\t);\n\treturn ordered.map((claim) => {\n\t\tconst evaluation = evaluations.get(claim.claimId);\n\t\tif (\n\t\t\t!evaluation ||\n\t\t\t(!isBlockingVerdict(evaluation.verdict) &&\n\t\t\t\tevaluation.verdict !== \"satisfied\" &&\n\t\t\t\tevaluation.verdict !== \"waived\")\n\t\t) {\n\t\t\tthrow new ClaimGraphError(\"invalid_input\", \"Blocking explanation requires a valid evaluation for every claim\");\n\t\t}\n\t\tconst inputs = claim.satisfaction.inputs.filter((id) => claims.get(id)?.severity === \"required\").sort(compareIds);\n\t\tconst inferredLocal =\n\t\t\t(evaluation.verdict === \"violated\" && evaluation.observationIds.length > 0) ||\n\t\t\t(evaluation.verdict === \"incomplete_scope\" && claim.scopeSensitive === true);\n\t\treturn {\n\t\t\tid: claim.claimId,\n\t\t\trule: claim.satisfaction.rule,\n\t\t\tinputs,\n\t\t\tclosed: !isBlockingVerdict(evaluation.verdict),\n\t\t\tlocal:\n\t\t\t\tinputs.length === 0 ||\n\t\t\t\tevaluation.blockingOrigin === \"local\" ||\n\t\t\t\t(evaluation.blockingOrigin === undefined && inferredLocal),\n\t\t};\n\t});\n}\nfunction reachableNodes(ordered: CutNode[], roots: readonly string[]): CutNode[] {\n\tconst byId = new Map(ordered.map((node) => [node.id, node]));\n\tconst reachable = new Set<string>();\n\tconst pending = [...roots];\n\twhile (pending.length) {\n\t\tconst id = pending.pop();\n\t\tif (id === undefined || reachable.has(id)) continue;\n\t\treachable.add(id);\n\t\tconst node = byId.get(id);\n\t\tif (node && !node.closed) pending.push(...node.inputs);\n\t}\n\treturn ordered.filter((node) => reachable.has(node.id));\n}\nfunction step(budget: CutSearchBudget): void {\n\tif (budget.operations >= MAX_CUT_OPERATIONS) throw new CutSearchLimit();\n\tbudget.operations++;\n}\nfunction prune(candidates: Iterable<Cut>, budget: CutSearchBudget): Family {\n\tlet kept: Cut[] = [];\n\tfor (const candidate of candidates) {\n\t\tstep(budget);\n\t\tconst set = new Set(candidate);\n\t\tif (\n\t\t\tkept.some((other) => {\n\t\t\t\tstep(budget);\n\t\t\t\treturn other.every((id) => set.has(id));\n\t\t\t})\n\t\t)\n\t\t\tcontinue;\n\t\tkept = kept.filter((other) => {\n\t\t\tstep(budget);\n\t\t\tconst existing = new Set(other);\n\t\t\treturn !candidate.every((id) => existing.has(id));\n\t\t});\n\t\tkept.push(candidate);\n\t\tif (kept.length > MAX_BLOCKING_CUT_CANDIDATES) throw new CutSearchLimit();\n\t}\n\treturn kept.sort(compareCuts);\n}\nfunction combineAll(families: readonly Family[], budget: CutSearchBudget): Family {\n\tlet result: Family = [[]];\n\tfor (const family of families) result = prune(unions(result, family), budget);\n\treturn result;\n}\nfunction exactCut(nodes: readonly CutNode[], roots: readonly string[], budget: CutSearchBudget): Cut {\n\tconst families = new Map<string, Family>();\n\tfor (const node of nodes) {\n\t\tif (node.closed) {\n\t\t\tfamilies.set(node.id, [[]]);\n\t\t\tcontinue;\n\t\t}\n\t\tconst children = node.inputs.map((id) => families.get(id) ?? []);\n\t\tlet family: Family = [[]];\n\t\tif (children.length) {\n\t\t\tfamily = node.rule === \"all\" ? combineAll(children, budget) : prune(alternatives(children), budget);\n\t\t}\n\t\tif (node.local) family = prune(unions(family, [[node.id]]), budget);\n\t\tfamilies.set(node.id, family);\n\t}\n\treturn (\n\t\tcombineAll(\n\t\t\troots.map((id) => families.get(id) ?? []),\n\t\t\tbudget,\n\t\t)[0] ?? []\n\t);\n}\nfunction greedyCut(nodes: readonly CutNode[], roots: readonly string[]): Cut {\n\tconst cuts = new Map<string, Cut>();\n\tfor (const node of nodes) {\n\t\tif (node.closed) {\n\t\t\tcuts.set(node.id, []);\n\t\t\tcontinue;\n\t\t}\n\t\tconst children = node.inputs.map((id) => cuts.get(id) ?? []);\n\t\tconst childCut = node.rule === \"all\" ? union(children) : ([...children].sort(compareCuts)[0] ?? []);\n\t\tcuts.set(node.id, node.local ? union([childCut, [node.id]]) : childCut);\n\t}\n\treturn union(roots.map((id) => cuts.get(id) ?? []));\n}\n\n/** Prefer this API over the legacy array-only projection when optimality matters. */\nexport function explainBlockingCut(\n\tclaims: ReadonlyMap<string, ClaimNode>,\n\tevaluations: ReadonlyMap<string, ClaimClosureEvaluation>,\n\trootIds: readonly string[],\n): BlockingCutExplanation {\n\tconst roots = [...new Set(rootIds)].sort(compareIds);\n\tfor (const id of roots) if (!claims.has(id)) throw new ClaimGraphError(\"unknown_input\", \"Unknown blocking root\");\n\tconst nodes = reachableNodes(cutNodes(claims, evaluations), roots);\n\tconst budget: CutSearchBudget = { operations: 0 };\n\tlet claimIds: Cut;\n\tlet truncated = false;\n\ttry {\n\t\tclaimIds = exactCut(nodes, roots, budget);\n\t} catch (error) {\n\t\tif (!(error instanceof CutSearchLimit)) throw error;\n\t\ttruncated = true;\n\t\tclaimIds = greedyCut(nodes, roots);\n\t}\n\tconst selected = new Set(claimIds);\n\treturn Object.freeze({\n\t\tclaimIds: Object.freeze([...claimIds]),\n\t\talgorithm: truncated ? \"greedy\" : \"exact-antichain\",\n\t\toptimality: truncated ? \"not-proven\" : \"minimum\",\n\t\ttruncated,\n\t\texploredStates: budget.operations,\n\t\tlocalClaimIds: Object.freeze(\n\t\t\tnodes\n\t\t\t\t.flatMap((node) => (node.local && node.inputs.length > 0 && selected.has(node.id) ? [node.id] : []))\n\t\t\t\t.sort(compareIds),\n\t\t),\n\t});\n}\n\n/** Compatibility name: on a bounded-search fallback this array is not guaranteed minimal. */\nexport function minimalBlockingCut(\n\tclaims: ReadonlyMap<string, ClaimNode>,\n\tevaluations: ReadonlyMap<string, ClaimClosureEvaluation>,\n\trootIds: readonly string[],\n): Cut {\n\treturn explainBlockingCut(claims, evaluations, rootIds).claimIds;\n}\n"]}
@@ -1,16 +1,12 @@
1
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.
2
+ * Bounded blocking explanations for a validated all/any claim DAG.
3
+ * Shared subgraphs require alternative repair sets, not a local cheapest branch.
4
+ * Exact antichains can grow exponentially; bounds fall back to a deterministic,
5
+ * not-proven explanation. Set-operation cost also depends on output-set size.
6
+ * A repair is an explanatory obligation, never permission to satisfy a claim.
13
7
  */
8
+ import { ClaimGraphError, topologicalClaimOrder } from "./claim-graph.js";
9
+ import { CLAIM_GRAPH_SCHEMA_VERSION, } from "./claim-types.js";
14
10
  const BLOCKING_VERDICTS = [
15
11
  "violated",
16
12
  "stale",
@@ -18,10 +14,14 @@ const BLOCKING_VERDICTS = [
18
14
  "insufficient_trust",
19
15
  "missing",
20
16
  ];
17
+ export const MAX_BLOCKING_CUT_CANDIDATES = 128;
18
+ const MAX_CUT_OPERATIONS = 65536;
19
+ class CutSearchLimit extends Error {
20
+ }
21
21
  export function isBlockingVerdict(verdict) {
22
22
  return BLOCKING_VERDICTS.includes(verdict);
23
23
  }
24
- function compareCodeUnits(left, right) {
24
+ function compareIds(left, right) {
25
25
  if (left === right)
26
26
  return 0;
27
27
  return left < right ? -1 : 1;
@@ -29,40 +29,160 @@ function compareCodeUnits(left, right) {
29
29
  function compareCuts(left, right) {
30
30
  if (left.length !== right.length)
31
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)
32
+ for (let i = 0; i < left.length; i++) {
33
+ const order = compareIds(left[i], right[i]);
34
+ if (order)
35
35
  return order;
36
36
  }
37
37
  return 0;
38
38
  }
39
- function unionSorted(sets) {
40
- return [...new Set(sets.flat())].sort(compareCodeUnits);
39
+ function union(sets) {
40
+ return [...new Set(sets.flat())].sort(compareIds);
41
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];
42
+ function* unions(left, right) {
43
+ for (const a of left)
44
+ for (const b of right)
45
+ yield union([a, b]);
46
+ }
47
+ function* alternatives(families) {
48
+ for (const family of families)
49
+ yield* family;
50
+ }
51
+ function cutNodes(claims, evaluations) {
52
+ const ordered = topologicalClaimOrder({ schemaVersion: CLAIM_GRAPH_SCHEMA_VERSION, claims: [...claims.values()] }, claims);
53
+ return ordered.map((claim) => {
54
+ const evaluation = evaluations.get(claim.claimId);
55
+ if (!evaluation ||
56
+ (!isBlockingVerdict(evaluation.verdict) &&
57
+ evaluation.verdict !== "satisfied" &&
58
+ evaluation.verdict !== "waived")) {
59
+ throw new ClaimGraphError("invalid_input", "Blocking explanation requires a valid evaluation for every claim");
60
+ }
61
+ const inputs = claim.satisfaction.inputs.filter((id) => claims.get(id)?.severity === "required").sort(compareIds);
62
+ const inferredLocal = (evaluation.verdict === "violated" && evaluation.observationIds.length > 0) ||
63
+ (evaluation.verdict === "incomplete_scope" && claim.scopeSensitive === true);
64
+ return {
65
+ id: claim.claimId,
66
+ rule: claim.satisfaction.rule,
67
+ inputs,
68
+ closed: !isBlockingVerdict(evaluation.verdict),
69
+ local: inputs.length === 0 ||
70
+ evaluation.blockingOrigin === "local" ||
71
+ (evaluation.blockingOrigin === undefined && inferredLocal),
72
+ };
73
+ });
74
+ }
75
+ function reachableNodes(ordered, roots) {
76
+ const byId = new Map(ordered.map((node) => [node.id, node]));
77
+ const reachable = new Set();
78
+ const pending = [...roots];
79
+ while (pending.length) {
80
+ const id = pending.pop();
81
+ if (id === undefined || reachable.has(id))
82
+ continue;
83
+ reachable.add(id);
84
+ const node = byId.get(id);
85
+ if (node && !node.closed)
86
+ pending.push(...node.inputs);
87
+ }
88
+ return ordered.filter((node) => reachable.has(node.id));
89
+ }
90
+ function step(budget) {
91
+ if (budget.operations >= MAX_CUT_OPERATIONS)
92
+ throw new CutSearchLimit();
93
+ budget.operations++;
94
+ }
95
+ function prune(candidates, budget) {
96
+ let kept = [];
97
+ for (const candidate of candidates) {
98
+ step(budget);
99
+ const set = new Set(candidate);
100
+ if (kept.some((other) => {
101
+ step(budget);
102
+ return other.every((id) => set.has(id));
103
+ }))
104
+ continue;
105
+ kept = kept.filter((other) => {
106
+ step(budget);
107
+ const existing = new Set(other);
108
+ return !candidate.every((id) => existing.has(id));
109
+ });
110
+ kept.push(candidate);
111
+ if (kept.length > MAX_BLOCKING_CUT_CANDIDATES)
112
+ throw new CutSearchLimit();
113
+ }
114
+ return kept.sort(compareCuts);
115
+ }
116
+ function combineAll(families, budget) {
117
+ let result = [[]];
118
+ for (const family of families)
119
+ result = prune(unions(result, family), budget);
120
+ return result;
121
+ }
122
+ function exactCut(nodes, roots, budget) {
123
+ const families = new Map();
124
+ for (const node of nodes) {
125
+ if (node.closed) {
126
+ families.set(node.id, [[]]);
127
+ continue;
128
+ }
129
+ const children = node.inputs.map((id) => families.get(id) ?? []);
130
+ let family = [[]];
131
+ if (children.length) {
132
+ family = node.rule === "all" ? combineAll(children, budget) : prune(alternatives(children), budget);
133
+ }
134
+ if (node.local)
135
+ family = prune(unions(family, [[node.id]]), budget);
136
+ families.set(node.id, family);
137
+ }
138
+ return (combineAll(roots.map((id) => families.get(id) ?? []), budget)[0] ?? []);
139
+ }
140
+ function greedyCut(nodes, roots) {
141
+ const cuts = new Map();
142
+ for (const node of nodes) {
143
+ if (node.closed) {
144
+ cuts.set(node.id, []);
145
+ continue;
62
146
  }
63
- memo.set(claimId, cut);
64
- return cut;
65
- };
66
- return unionSorted(rootIds.map(cutFor));
147
+ const children = node.inputs.map((id) => cuts.get(id) ?? []);
148
+ const childCut = node.rule === "all" ? union(children) : ([...children].sort(compareCuts)[0] ?? []);
149
+ cuts.set(node.id, node.local ? union([childCut, [node.id]]) : childCut);
150
+ }
151
+ return union(roots.map((id) => cuts.get(id) ?? []));
152
+ }
153
+ /** Prefer this API over the legacy array-only projection when optimality matters. */
154
+ export function explainBlockingCut(claims, evaluations, rootIds) {
155
+ const roots = [...new Set(rootIds)].sort(compareIds);
156
+ for (const id of roots)
157
+ if (!claims.has(id))
158
+ throw new ClaimGraphError("unknown_input", "Unknown blocking root");
159
+ const nodes = reachableNodes(cutNodes(claims, evaluations), roots);
160
+ const budget = { operations: 0 };
161
+ let claimIds;
162
+ let truncated = false;
163
+ try {
164
+ claimIds = exactCut(nodes, roots, budget);
165
+ }
166
+ catch (error) {
167
+ if (!(error instanceof CutSearchLimit))
168
+ throw error;
169
+ truncated = true;
170
+ claimIds = greedyCut(nodes, roots);
171
+ }
172
+ const selected = new Set(claimIds);
173
+ return Object.freeze({
174
+ claimIds: Object.freeze([...claimIds]),
175
+ algorithm: truncated ? "greedy" : "exact-antichain",
176
+ optimality: truncated ? "not-proven" : "minimum",
177
+ truncated,
178
+ exploredStates: budget.operations,
179
+ localClaimIds: Object.freeze(nodes
180
+ .flatMap((node) => (node.local && node.inputs.length > 0 && selected.has(node.id) ? [node.id] : []))
181
+ .sort(compareIds)),
182
+ });
183
+ }
184
+ /** Compatibility name: on a bounded-search fallback this array is not guaranteed minimal. */
185
+ export function minimalBlockingCut(claims, evaluations, rootIds) {
186
+ return explainBlockingCut(claims, evaluations, rootIds).claimIds;
67
187
  }
68
188
  //# sourceMappingURL=claim-blocking-cut.js.map
@@ -1 +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"]}
1
+ {"version":3,"file":"claim-blocking-cut.js","sourceRoot":"","sources":["../../src/claims/claim-blocking-cut.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Bounded blocking explanations for a validated all/any claim DAG.\n * Shared subgraphs require alternative repair sets, not a local cheapest branch.\n * Exact antichains can grow exponentially; bounds fall back to a deterministic,\n * not-proven explanation. Set-operation cost also depends on output-set size.\n * A repair is an explanatory obligation, never permission to satisfy a claim.\n */\nimport { ClaimGraphError, topologicalClaimOrder } from \"./claim-graph.ts\";\nimport {\n\ttype BlockingCutExplanation,\n\tCLAIM_GRAPH_SCHEMA_VERSION,\n\ttype ClaimClosureEvaluation,\n\ttype ClaimNode,\n\ttype ClaimVerdict,\n} 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];\nexport const MAX_BLOCKING_CUT_CANDIDATES = 128;\nconst MAX_CUT_OPERATIONS = 65536;\nclass CutSearchLimit extends Error {}\ntype Cut = readonly string[];\ntype Family = readonly Cut[];\ninterface CutNode {\n\treadonly id: string;\n\treadonly rule: \"all\" | \"any\";\n\treadonly inputs: readonly string[];\n\treadonly closed: boolean;\n\treadonly local: boolean;\n}\n/** Search-owned mutable accounting, including work before a limit is reached. */\ninterface CutSearchBudget {\n\toperations: number;\n}\n\nexport function isBlockingVerdict(verdict: ClaimVerdict): boolean {\n\treturn BLOCKING_VERDICTS.includes(verdict);\n}\nfunction compareIds(left: string, right: string): number {\n\tif (left === right) return 0;\n\treturn left < right ? -1 : 1;\n}\nfunction compareCuts(left: Cut, right: Cut): number {\n\tif (left.length !== right.length) return left.length - right.length;\n\tfor (let i = 0; i < left.length; i++) {\n\t\tconst order = compareIds(left[i], right[i]);\n\t\tif (order) return order;\n\t}\n\treturn 0;\n}\nfunction union(sets: readonly Cut[]): Cut {\n\treturn [...new Set(sets.flat())].sort(compareIds);\n}\nfunction* unions(left: Family, right: Family): Generator<Cut> {\n\tfor (const a of left) for (const b of right) yield union([a, b]);\n}\nfunction* alternatives(families: readonly Family[]): Generator<Cut> {\n\tfor (const family of families) yield* family;\n}\n\nfunction cutNodes(\n\tclaims: ReadonlyMap<string, ClaimNode>,\n\tevaluations: ReadonlyMap<string, ClaimClosureEvaluation>,\n): CutNode[] {\n\tconst ordered = topologicalClaimOrder(\n\t\t{ schemaVersion: CLAIM_GRAPH_SCHEMA_VERSION, claims: [...claims.values()] },\n\t\tclaims,\n\t);\n\treturn ordered.map((claim) => {\n\t\tconst evaluation = evaluations.get(claim.claimId);\n\t\tif (\n\t\t\t!evaluation ||\n\t\t\t(!isBlockingVerdict(evaluation.verdict) &&\n\t\t\t\tevaluation.verdict !== \"satisfied\" &&\n\t\t\t\tevaluation.verdict !== \"waived\")\n\t\t) {\n\t\t\tthrow new ClaimGraphError(\"invalid_input\", \"Blocking explanation requires a valid evaluation for every claim\");\n\t\t}\n\t\tconst inputs = claim.satisfaction.inputs.filter((id) => claims.get(id)?.severity === \"required\").sort(compareIds);\n\t\tconst inferredLocal =\n\t\t\t(evaluation.verdict === \"violated\" && evaluation.observationIds.length > 0) ||\n\t\t\t(evaluation.verdict === \"incomplete_scope\" && claim.scopeSensitive === true);\n\t\treturn {\n\t\t\tid: claim.claimId,\n\t\t\trule: claim.satisfaction.rule,\n\t\t\tinputs,\n\t\t\tclosed: !isBlockingVerdict(evaluation.verdict),\n\t\t\tlocal:\n\t\t\t\tinputs.length === 0 ||\n\t\t\t\tevaluation.blockingOrigin === \"local\" ||\n\t\t\t\t(evaluation.blockingOrigin === undefined && inferredLocal),\n\t\t};\n\t});\n}\nfunction reachableNodes(ordered: CutNode[], roots: readonly string[]): CutNode[] {\n\tconst byId = new Map(ordered.map((node) => [node.id, node]));\n\tconst reachable = new Set<string>();\n\tconst pending = [...roots];\n\twhile (pending.length) {\n\t\tconst id = pending.pop();\n\t\tif (id === undefined || reachable.has(id)) continue;\n\t\treachable.add(id);\n\t\tconst node = byId.get(id);\n\t\tif (node && !node.closed) pending.push(...node.inputs);\n\t}\n\treturn ordered.filter((node) => reachable.has(node.id));\n}\nfunction step(budget: CutSearchBudget): void {\n\tif (budget.operations >= MAX_CUT_OPERATIONS) throw new CutSearchLimit();\n\tbudget.operations++;\n}\nfunction prune(candidates: Iterable<Cut>, budget: CutSearchBudget): Family {\n\tlet kept: Cut[] = [];\n\tfor (const candidate of candidates) {\n\t\tstep(budget);\n\t\tconst set = new Set(candidate);\n\t\tif (\n\t\t\tkept.some((other) => {\n\t\t\t\tstep(budget);\n\t\t\t\treturn other.every((id) => set.has(id));\n\t\t\t})\n\t\t)\n\t\t\tcontinue;\n\t\tkept = kept.filter((other) => {\n\t\t\tstep(budget);\n\t\t\tconst existing = new Set(other);\n\t\t\treturn !candidate.every((id) => existing.has(id));\n\t\t});\n\t\tkept.push(candidate);\n\t\tif (kept.length > MAX_BLOCKING_CUT_CANDIDATES) throw new CutSearchLimit();\n\t}\n\treturn kept.sort(compareCuts);\n}\nfunction combineAll(families: readonly Family[], budget: CutSearchBudget): Family {\n\tlet result: Family = [[]];\n\tfor (const family of families) result = prune(unions(result, family), budget);\n\treturn result;\n}\nfunction exactCut(nodes: readonly CutNode[], roots: readonly string[], budget: CutSearchBudget): Cut {\n\tconst families = new Map<string, Family>();\n\tfor (const node of nodes) {\n\t\tif (node.closed) {\n\t\t\tfamilies.set(node.id, [[]]);\n\t\t\tcontinue;\n\t\t}\n\t\tconst children = node.inputs.map((id) => families.get(id) ?? []);\n\t\tlet family: Family = [[]];\n\t\tif (children.length) {\n\t\t\tfamily = node.rule === \"all\" ? combineAll(children, budget) : prune(alternatives(children), budget);\n\t\t}\n\t\tif (node.local) family = prune(unions(family, [[node.id]]), budget);\n\t\tfamilies.set(node.id, family);\n\t}\n\treturn (\n\t\tcombineAll(\n\t\t\troots.map((id) => families.get(id) ?? []),\n\t\t\tbudget,\n\t\t)[0] ?? []\n\t);\n}\nfunction greedyCut(nodes: readonly CutNode[], roots: readonly string[]): Cut {\n\tconst cuts = new Map<string, Cut>();\n\tfor (const node of nodes) {\n\t\tif (node.closed) {\n\t\t\tcuts.set(node.id, []);\n\t\t\tcontinue;\n\t\t}\n\t\tconst children = node.inputs.map((id) => cuts.get(id) ?? []);\n\t\tconst childCut = node.rule === \"all\" ? union(children) : ([...children].sort(compareCuts)[0] ?? []);\n\t\tcuts.set(node.id, node.local ? union([childCut, [node.id]]) : childCut);\n\t}\n\treturn union(roots.map((id) => cuts.get(id) ?? []));\n}\n\n/** Prefer this API over the legacy array-only projection when optimality matters. */\nexport function explainBlockingCut(\n\tclaims: ReadonlyMap<string, ClaimNode>,\n\tevaluations: ReadonlyMap<string, ClaimClosureEvaluation>,\n\trootIds: readonly string[],\n): BlockingCutExplanation {\n\tconst roots = [...new Set(rootIds)].sort(compareIds);\n\tfor (const id of roots) if (!claims.has(id)) throw new ClaimGraphError(\"unknown_input\", \"Unknown blocking root\");\n\tconst nodes = reachableNodes(cutNodes(claims, evaluations), roots);\n\tconst budget: CutSearchBudget = { operations: 0 };\n\tlet claimIds: Cut;\n\tlet truncated = false;\n\ttry {\n\t\tclaimIds = exactCut(nodes, roots, budget);\n\t} catch (error) {\n\t\tif (!(error instanceof CutSearchLimit)) throw error;\n\t\ttruncated = true;\n\t\tclaimIds = greedyCut(nodes, roots);\n\t}\n\tconst selected = new Set(claimIds);\n\treturn Object.freeze({\n\t\tclaimIds: Object.freeze([...claimIds]),\n\t\talgorithm: truncated ? \"greedy\" : \"exact-antichain\",\n\t\toptimality: truncated ? \"not-proven\" : \"minimum\",\n\t\ttruncated,\n\t\texploredStates: budget.operations,\n\t\tlocalClaimIds: Object.freeze(\n\t\t\tnodes\n\t\t\t\t.flatMap((node) => (node.local && node.inputs.length > 0 && selected.has(node.id) ? [node.id] : []))\n\t\t\t\t.sort(compareIds),\n\t\t),\n\t});\n}\n\n/** Compatibility name: on a bounded-search fallback this array is not guaranteed minimal. */\nexport function minimalBlockingCut(\n\tclaims: ReadonlyMap<string, ClaimNode>,\n\tevaluations: ReadonlyMap<string, ClaimClosureEvaluation>,\n\trootIds: readonly string[],\n): Cut {\n\treturn explainBlockingCut(claims, evaluations, rootIds).claimIds;\n}\n"]}
@@ -1 +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"]}
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,EAIvB,MAAM,kBAAkB,CAAC;AAmK1B,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,iBAAiB,GAAG,kBAAkB,CAmEjF","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 { explainBlockingCut, isBlockingVerdict } 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\ttype WitnessIndependencePolicy,\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\tindependence: WitnessIndependencePolicy,\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 requiredWitnesses = claim.requiredWitnesses ?? 1;\n\tconst attributable = trusted.filter(\n\t\t(observation) =>\n\t\t\tindependence === \"legacy-observation-id\" ||\n\t\t\trequiredWitnesses === 1 ||\n\t\t\tBoolean(observation.independenceGroup?.trim()),\n\t);\n\tconst groups = new Set(\n\t\tattributable.map(\n\t\t\t(observation) =>\n\t\t\t\tobservation.independenceGroup ??\n\t\t\t\t(independence === \"legacy-observation-id\" ? observation.observationId : undefined),\n\t\t),\n\t);\n\tif (groups.size >= requiredWitnesses) 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\tconst witnessIndependence =\n\t\tinput.witnessIndependence === undefined ? \"legacy-observation-id\" : input.witnessIndependence;\n\tif (witnessIndependence !== \"legacy-observation-id\" && witnessIndependence !== \"explicit-groups\") {\n\t\tthrow new ClaimGraphError(\"invalid_input\", \"Unknown witness independence policy\");\n\t}\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, witnessIndependence)\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\t...(isBlockingVerdict(verdict)\n\t\t\t\t\t? {\n\t\t\t\t\t\t\tblockingOrigin:\n\t\t\t\t\t\t\t\tinputs.length === 0 ||\n\t\t\t\t\t\t\t\tclaimWitnesses.violating.length > 0 ||\n\t\t\t\t\t\t\t\t(claim.scopeSensitive === true && input.workspaceCompleteness !== \"complete\")\n\t\t\t\t\t\t\t\t\t? (\"local\" as const)\n\t\t\t\t\t\t\t\t\t: (\"children\" as const),\n\t\t\t\t\t\t}\n\t\t\t\t\t: {}),\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\tconst blockingCut = explainBlockingCut(claims, evaluations, blockingRoots);\n\treturn Object.freeze({\n\t\tverdict: globalVerdict(requiredRoots, input),\n\t\twitnessIndependence,\n\t\tclaimEvaluations: Object.freeze(ordered),\n\t\tblockingClaimIds: Object.freeze(blockingClaimIds),\n\t\tminimalBlockingCut: blockingCut.claimIds,\n\t\tblockingCut,\n\t\tunresolvedEffectIds: Object.freeze([...input.unresolvedEffectIds].sort(compareCodeUnits)),\n\t\tworkspaceCompleteness: input.workspaceCompleteness,\n\t});\n}\n"]}
@@ -25,7 +25,7 @@
25
25
  * is what `any` means — while `blockingClaimIds` still lists every required
26
26
  * claim on a blocking path so the cause is explainable.
27
27
  */
28
- import { isBlockingVerdict, minimalBlockingCut } from "./claim-blocking-cut.js";
28
+ import { explainBlockingCut, isBlockingVerdict } from "./claim-blocking-cut.js";
29
29
  import { ClaimGraphError, rootClaimIds, topologicalClaimOrder, validateClaimGraph } from "./claim-graph.js";
30
30
  import { CLAIM_VERDICT_PRECEDENCE, OBSERVATION_TRUST_RANK, } from "./claim-types.js";
31
31
  const ISO_TIMESTAMP = /^\d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}\.\d{3}Z$/;
@@ -78,13 +78,18 @@ function ids(observations) {
78
78
  return observations.map((observation) => observation.observationId).sort(compareCodeUnits);
79
79
  }
80
80
  /** Steps 3–4 for a leaf claim. */
81
- function evaluateLeaf(claim, witnesses) {
81
+ function evaluateLeaf(claim, witnesses, independence) {
82
82
  if (witnesses.violating.length > 0)
83
83
  return { verdict: "violated", observationIds: ids(witnesses.violating) };
84
84
  const floor = OBSERVATION_TRUST_RANK[claim.trustFloor];
85
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))
86
+ const requiredWitnesses = claim.requiredWitnesses ?? 1;
87
+ const attributable = trusted.filter((observation) => independence === "legacy-observation-id" ||
88
+ requiredWitnesses === 1 ||
89
+ Boolean(observation.independenceGroup?.trim()));
90
+ const groups = new Set(attributable.map((observation) => observation.independenceGroup ??
91
+ (independence === "legacy-observation-id" ? observation.observationId : undefined)));
92
+ if (groups.size >= requiredWitnesses)
88
93
  return { verdict: "satisfied", observationIds: ids(trusted) };
89
94
  if (trusted.length > 0)
90
95
  return { verdict: "missing", observationIds: ids(trusted) };
@@ -159,6 +164,10 @@ function globalVerdict(requiredRoots, input) {
159
164
  }
160
165
  export function evaluateProofClosure(input) {
161
166
  const claims = validateClaimGraph(input.graph);
167
+ const witnessIndependence = input.witnessIndependence === undefined ? "legacy-observation-id" : input.witnessIndependence;
168
+ if (witnessIndependence !== "legacy-observation-id" && witnessIndependence !== "explicit-groups") {
169
+ throw new ClaimGraphError("invalid_input", "Unknown witness independence policy");
170
+ }
162
171
  requireTimestamp(input.now, "now");
163
172
  const witnesses = indexWitnesses(input, claims);
164
173
  const waivers = indexWaivers(input, claims);
@@ -167,7 +176,7 @@ export function evaluateProofClosure(input) {
167
176
  const claimWitnesses = witnesses.get(claim.claimId);
168
177
  const inputs = requiredInputs(claim, claims);
169
178
  const local = inputs.length === 0
170
- ? evaluateLeaf(claim, claimWitnesses)
179
+ ? evaluateLeaf(claim, claimWitnesses, witnessIndependence)
171
180
  : evaluateComposite(claim, inputs, claimWitnesses, evaluations);
172
181
  let verdict = local.verdict;
173
182
  if (verdict !== "violated" && claim.scopeSensitive === true && input.workspaceCompleteness !== "complete") {
@@ -184,6 +193,15 @@ export function evaluateProofClosure(input) {
184
193
  reasonCode: reasonFor(verdict),
185
194
  observationIds: Object.freeze([...local.observationIds]),
186
195
  ...(waived ? { waiverId: waiver.waiverId } : {}),
196
+ ...(isBlockingVerdict(verdict)
197
+ ? {
198
+ blockingOrigin: inputs.length === 0 ||
199
+ claimWitnesses.violating.length > 0 ||
200
+ (claim.scopeSensitive === true && input.workspaceCompleteness !== "complete")
201
+ ? "local"
202
+ : "children",
203
+ }
204
+ : {}),
187
205
  }));
188
206
  }
189
207
  const graphOrder = input.graph.claims.map((claim) => claim.claimId);
@@ -195,11 +213,14 @@ export function evaluateProofClosure(input) {
195
213
  .filter((evaluation) => isBlockingVerdict(evaluation.verdict))
196
214
  .map((evaluation) => evaluation.claimId);
197
215
  const blockingClaimIds = blockingPathClaimIds(claims, evaluations, blockingRoots, graphOrder);
216
+ const blockingCut = explainBlockingCut(claims, evaluations, blockingRoots);
198
217
  return Object.freeze({
199
218
  verdict: globalVerdict(requiredRoots, input),
219
+ witnessIndependence,
200
220
  claimEvaluations: Object.freeze(ordered),
201
221
  blockingClaimIds: Object.freeze(blockingClaimIds),
202
- minimalBlockingCut: Object.freeze([...minimalBlockingCut(claims, evaluations, blockingRoots)]),
222
+ minimalBlockingCut: blockingCut.claimIds,
223
+ blockingCut,
203
224
  unresolvedEffectIds: Object.freeze([...input.unresolvedEffectIds].sort(compareCodeUnits)),
204
225
  workspaceCompleteness: input.workspaceCompleteness,
205
226
  });
@@ -1 +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"]}
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 { explainBlockingCut, isBlockingVerdict } 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\ttype WitnessIndependencePolicy,\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\tindependence: WitnessIndependencePolicy,\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 requiredWitnesses = claim.requiredWitnesses ?? 1;\n\tconst attributable = trusted.filter(\n\t\t(observation) =>\n\t\t\tindependence === \"legacy-observation-id\" ||\n\t\t\trequiredWitnesses === 1 ||\n\t\t\tBoolean(observation.independenceGroup?.trim()),\n\t);\n\tconst groups = new Set(\n\t\tattributable.map(\n\t\t\t(observation) =>\n\t\t\t\tobservation.independenceGroup ??\n\t\t\t\t(independence === \"legacy-observation-id\" ? observation.observationId : undefined),\n\t\t),\n\t);\n\tif (groups.size >= requiredWitnesses) 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\tconst witnessIndependence =\n\t\tinput.witnessIndependence === undefined ? \"legacy-observation-id\" : input.witnessIndependence;\n\tif (witnessIndependence !== \"legacy-observation-id\" && witnessIndependence !== \"explicit-groups\") {\n\t\tthrow new ClaimGraphError(\"invalid_input\", \"Unknown witness independence policy\");\n\t}\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, witnessIndependence)\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\t...(isBlockingVerdict(verdict)\n\t\t\t\t\t? {\n\t\t\t\t\t\t\tblockingOrigin:\n\t\t\t\t\t\t\t\tinputs.length === 0 ||\n\t\t\t\t\t\t\t\tclaimWitnesses.violating.length > 0 ||\n\t\t\t\t\t\t\t\t(claim.scopeSensitive === true && input.workspaceCompleteness !== \"complete\")\n\t\t\t\t\t\t\t\t\t? (\"local\" as const)\n\t\t\t\t\t\t\t\t\t: (\"children\" as const),\n\t\t\t\t\t\t}\n\t\t\t\t\t: {}),\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\tconst blockingCut = explainBlockingCut(claims, evaluations, blockingRoots);\n\treturn Object.freeze({\n\t\tverdict: globalVerdict(requiredRoots, input),\n\t\twitnessIndependence,\n\t\tclaimEvaluations: Object.freeze(ordered),\n\t\tblockingClaimIds: Object.freeze(blockingClaimIds),\n\t\tminimalBlockingCut: blockingCut.claimIds,\n\t\tblockingCut,\n\t\tunresolvedEffectIds: Object.freeze([...input.unresolvedEffectIds].sort(compareCodeUnits)),\n\t\tworkspaceCompleteness: input.workspaceCompleteness,\n\t});\n}\n"]}
@@ -53,7 +53,7 @@ export interface ObservationNode {
53
53
  /** ISO-8601 instant after which the observation no longer qualifies. */
54
54
  readonly validUntil?: string;
55
55
  readonly invalidationKeys?: readonly string[];
56
- /** Witnesses sharing a group count once; defaults to the observation id. */
56
+ /** Witnesses sharing a group count once. The explicit-groups policy requires this for multi-witness claims. */
57
57
  readonly independenceGroup?: string;
58
58
  }
59
59
  export interface WaiverNode {
@@ -68,9 +68,12 @@ export interface ClaimGraph {
68
68
  readonly schemaVersion: typeof CLAIM_GRAPH_SCHEMA_VERSION;
69
69
  readonly claims: readonly ClaimNode[];
70
70
  }
71
+ export type WitnessIndependencePolicy = "legacy-observation-id" | "explicit-groups";
71
72
  export interface ProofClosureInput {
72
73
  readonly graph: ClaimGraph;
73
74
  readonly observations: readonly ObservationNode[];
75
+ /** Compatibility default: legacy-observation-id. Strong profiles must opt into explicit-groups. */
76
+ readonly witnessIndependence?: WitnessIndependencePolicy;
74
77
  readonly waivers: readonly WaiverNode[];
75
78
  readonly sourceRoot: string;
76
79
  readonly environmentDigest: string;
@@ -88,14 +91,29 @@ export interface ClaimClosureEvaluation {
88
91
  /** Qualified observations that decided the verdict (violations, or counted witnesses). */
89
92
  readonly observationIds: readonly string[];
90
93
  readonly waiverId?: string;
94
+ /** Distinguish local obligations from a verdict derived only from children. */
95
+ readonly blockingOrigin?: "local" | "children";
96
+ }
97
+ export interface BlockingCutExplanation {
98
+ readonly claimIds: readonly string[];
99
+ readonly algorithm: "exact-antichain" | "greedy";
100
+ /** Minimum cardinality in this repair model only; fallback has no minimality guarantee. */
101
+ readonly optimality: "minimum" | "not-proven";
102
+ readonly truncated: boolean;
103
+ readonly exploredStates: number;
104
+ /** Selected composite-local obligations that child repairs alone cannot resolve. */
105
+ readonly localClaimIds: readonly string[];
91
106
  }
92
107
  export interface ProofClosureResult {
93
108
  readonly verdict: VerificationVerdict;
109
+ readonly witnessIndependence?: WitnessIndependencePolicy;
94
110
  readonly claimEvaluations: readonly ClaimClosureEvaluation[];
95
111
  /** Required claims that are not closed, in evaluation order. */
96
112
  readonly blockingClaimIds: readonly string[];
97
- /** Smallest leaf set whose closure would unblock every blocking root; sorted. */
113
+ /** Compatibility projection; consult blockingCut for bounded-search optimality and local causes. */
98
114
  readonly minimalBlockingCut: readonly string[];
115
+ /** Explanation only; never overrides the closure verdict or unresolved global effects. */
116
+ readonly blockingCut?: BlockingCutExplanation;
99
117
  readonly unresolvedEffectIds: readonly string[];
100
118
  readonly workspaceCompleteness: WorkspaceCompleteness;
101
119
  }
@@ -1 +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"]}
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. The explicit-groups policy requires this for multi-witness claims. */\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 type WitnessIndependencePolicy = \"legacy-observation-id\" | \"explicit-groups\";\n\nexport interface ProofClosureInput {\n\treadonly graph: ClaimGraph;\n\treadonly observations: readonly ObservationNode[];\n\t/** Compatibility default: legacy-observation-id. Strong profiles must opt into explicit-groups. */\n\treadonly witnessIndependence?: WitnessIndependencePolicy;\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\t/** Distinguish local obligations from a verdict derived only from children. */\n\treadonly blockingOrigin?: \"local\" | \"children\";\n}\n\nexport interface BlockingCutExplanation {\n\treadonly claimIds: readonly string[];\n\treadonly algorithm: \"exact-antichain\" | \"greedy\";\n\t/** Minimum cardinality in this repair model only; fallback has no minimality guarantee. */\n\treadonly optimality: \"minimum\" | \"not-proven\";\n\treadonly truncated: boolean;\n\treadonly exploredStates: number;\n\t/** Selected composite-local obligations that child repairs alone cannot resolve. */\n\treadonly localClaimIds: readonly string[];\n}\n\nexport interface ProofClosureResult {\n\treadonly verdict: VerificationVerdict;\n\treadonly witnessIndependence?: WitnessIndependencePolicy;\n\treadonly claimEvaluations: readonly ClaimClosureEvaluation[];\n\t/** Required claims that are not closed, in evaluation order. */\n\treadonly blockingClaimIds: readonly string[];\n\t/** Compatibility projection; consult blockingCut for bounded-search optimality and local causes. */\n\treadonly minimalBlockingCut: readonly string[];\n\t/** Explanation only; never overrides the closure verdict or unresolved global effects. */\n\treadonly blockingCut?: BlockingCutExplanation;\n\treadonly unresolvedEffectIds: readonly string[];\n\treadonly workspaceCompleteness: WorkspaceCompleteness;\n}\n"]}
@@ -1 +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"]}
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. The explicit-groups policy requires this for multi-witness claims. */\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 type WitnessIndependencePolicy = \"legacy-observation-id\" | \"explicit-groups\";\n\nexport interface ProofClosureInput {\n\treadonly graph: ClaimGraph;\n\treadonly observations: readonly ObservationNode[];\n\t/** Compatibility default: legacy-observation-id. Strong profiles must opt into explicit-groups. */\n\treadonly witnessIndependence?: WitnessIndependencePolicy;\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\t/** Distinguish local obligations from a verdict derived only from children. */\n\treadonly blockingOrigin?: \"local\" | \"children\";\n}\n\nexport interface BlockingCutExplanation {\n\treadonly claimIds: readonly string[];\n\treadonly algorithm: \"exact-antichain\" | \"greedy\";\n\t/** Minimum cardinality in this repair model only; fallback has no minimality guarantee. */\n\treadonly optimality: \"minimum\" | \"not-proven\";\n\treadonly truncated: boolean;\n\treadonly exploredStates: number;\n\t/** Selected composite-local obligations that child repairs alone cannot resolve. */\n\treadonly localClaimIds: readonly string[];\n}\n\nexport interface ProofClosureResult {\n\treadonly verdict: VerificationVerdict;\n\treadonly witnessIndependence?: WitnessIndependencePolicy;\n\treadonly claimEvaluations: readonly ClaimClosureEvaluation[];\n\t/** Required claims that are not closed, in evaluation order. */\n\treadonly blockingClaimIds: readonly string[];\n\t/** Compatibility projection; consult blockingCut for bounded-search optimality and local causes. */\n\treadonly minimalBlockingCut: readonly string[];\n\t/** Explanation only; never overrides the closure verdict or unresolved global effects. */\n\treadonly blockingCut?: BlockingCutExplanation;\n\treadonly unresolvedEffectIds: readonly string[];\n\treadonly workspaceCompleteness: WorkspaceCompleteness;\n}\n"]}
package/dist/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
- export { isBlockingVerdict, minimalBlockingCut } from "./claims/claim-blocking-cut.ts";
1
+ export { explainBlockingCut, isBlockingVerdict, MAX_BLOCKING_CUT_CANDIDATES, minimalBlockingCut, } from "./claims/claim-blocking-cut.ts";
2
2
  export { evaluateProofClosure } from "./claims/claim-closure.ts";
3
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";
4
+ export { type BlockingCutExplanation, 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 WitnessIndependencePolicy, type WorkspaceCompleteness, } from "./claims/claim-types.ts";
5
5
  export { reduceRuntimeDecision } from "./decision.ts";
6
6
  export { evaluateTask, ProtocolInvariantError } from "./evaluation.ts";
7
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";
@@ -1 +1 @@
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"]}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,kBAAkB,EAClB,iBAAiB,EACjB,2BAA2B,EAC3B,kBAAkB,GAClB,MAAM,gCAAgC,CAAC;AACxC,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,KAAK,sBAAsB,EAC3B,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,yBAAyB,EAC9B,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 {\n\texplainBlockingCut,\n\tisBlockingVerdict,\n\tMAX_BLOCKING_CUT_CANDIDATES,\n\tminimalBlockingCut,\n} 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\ttype BlockingCutExplanation,\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 WitnessIndependencePolicy,\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,4 +1,4 @@
1
- export { isBlockingVerdict, minimalBlockingCut } from "./claims/claim-blocking-cut.js";
1
+ export { explainBlockingCut, isBlockingVerdict, MAX_BLOCKING_CUT_CANDIDATES, minimalBlockingCut, } from "./claims/claim-blocking-cut.js";
2
2
  export { evaluateProofClosure } from "./claims/claim-closure.js";
3
3
  export { ClaimGraphError, canonicalClaimGraph, rootClaimIds, topologicalClaimOrder, validateClaimGraph, } from "./claims/claim-graph.js";
4
4
  export { CLAIM_GRAPH_SCHEMA_VERSION, CLAIM_VERDICT_PRECEDENCE, OBSERVATION_TRUST_RANK, } from "./claims/claim-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,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"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACN,kBAAkB,EAClB,iBAAiB,EACjB,2BAA2B,EAC3B,kBAAkB,GAClB,MAAM,gCAAgC,CAAC;AACxC,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,EAEN,0BAA0B,EAC1B,wBAAwB,EAQxB,sBAAsB,GAUtB,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 {\n\texplainBlockingCut,\n\tisBlockingVerdict,\n\tMAX_BLOCKING_CUT_CANDIDATES,\n\tminimalBlockingCut,\n} 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\ttype BlockingCutExplanation,\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 WitnessIndependencePolicy,\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",
3
+ "version": "0.98.4",
4
4
  "description": "Canonical OMK Run Protocol contracts and pure semantic reducers",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",