From 15330abbf89d18b3e8516f51fe2815b3d29bf26e Mon Sep 17 00:00:00 2001 From: Vibe Nuage Agent Date: Fri, 30 Oct 2026 16:53:30 +0000 Subject: [PATCH] feat(conformance): implement TC-SEM-01..03 semantic validators and TC-RC-01..05 recursive chain validators per RFC-0002 Co-authored-by: emmanuel-a-otchere --- src/contracts/semantic-integrity.ts | 197 +++++++++++++++++++++++++ src/fixtures/rc-bounded-high.json | 162 ++++++++++++++++++++ src/fixtures/rc-bounded-ok.json | 162 ++++++++++++++++++++ src/fixtures/rc-no-bound.json | 160 ++++++++++++++++++++ src/fixtures/rc-terminal-violated.json | 161 ++++++++++++++++++++ src/fixtures/rc-unbounded.json | 161 ++++++++++++++++++++ src/fixtures/sem-bad-urn.json | 157 ++++++++++++++++++++ src/fixtures/sem-dangling-source.json | 157 ++++++++++++++++++++ src/fixtures/sem-future-evidence.json | 157 ++++++++++++++++++++ src/runner.ts | 7 +- src/types.ts | 13 ++ tests/semantic.test.ts | 105 +++++++++++++ 12 files changed, 1598 insertions(+), 1 deletion(-) create mode 100644 src/contracts/semantic-integrity.ts create mode 100644 src/fixtures/rc-bounded-high.json create mode 100644 src/fixtures/rc-bounded-ok.json create mode 100644 src/fixtures/rc-no-bound.json create mode 100644 src/fixtures/rc-terminal-violated.json create mode 100644 src/fixtures/rc-unbounded.json create mode 100644 src/fixtures/sem-bad-urn.json create mode 100644 src/fixtures/sem-dangling-source.json create mode 100644 src/fixtures/sem-future-evidence.json create mode 100644 tests/semantic.test.ts diff --git a/src/contracts/semantic-integrity.ts b/src/contracts/semantic-integrity.ts new file mode 100644 index 0000000..e11e5b5 --- /dev/null +++ b/src/contracts/semantic-integrity.ts @@ -0,0 +1,197 @@ +/** + * Semantic integrity validators. Implements TC-SEM-01..03 (URN grammar, + * evidence-chain resolution, temporal ordering) and the RFC-0002 recursive + * chain test cases TC-RC-01..05. + * + * See: https://github.com/Source-Engineering/source-engineering-specifications/blob/main/compatibility-test-suite.md + * See: RFC-0002 (recursive evidence chains). + */ + +import type { Cots } from "../types.js"; +import { type Result, combine, fail, pass, warn } from "../result.js"; + +const URN_RE = /^urn:source-engineering:[a-z][a-z0-9-]*:[^\s]+$/; + +const ts = (value: string): number => Date.parse(value); + +/** TC-SEM-01: every identifier is a well-formed Source Engineering URN. */ +export const validateUrnGrammar = (cots: Cots): Result => { + const errors: Result[] = []; + const check = (id: string, where: string) => { + if (!URN_RE.test(id)) { + errors.push(fail("TC-SEM-01", `${where} has a malformed URN: '${id}'`)); + } + }; + + check(cots.cots_id, "cots_id"); + cots.inputs.source_artifacts.forEach((a, i) => check(a.id, `inputs.source_artifacts[${i}].id`)); + cots.context.constraints.forEach((c, i) => check(c.id, `context.constraints[${i}].id`)); + cots.decisions.forEach((d, i) => { + check(d.id, `decisions[${i}].id`); + check(d.basis_id, `decisions[${i}].basis_id`); + check(d.evidence_chain_ref, `decisions[${i}].evidence_chain_ref`); + }); + cots.evidence.evidence_chain.forEach((chain, i) => { + chain.decision_id && check(chain.decision_id, `evidence.evidence_chain[${i}].decision_id`); + for (const item of [...chain.supporting_evidence, ...chain.contradicting_evidence]) { + check(item.id, `evidence.evidence_chain[${i}] evidence item id`); + check(item.claim_id, `evidence.evidence_chain[${i}] evidence claim_id`); + check(item.source_id, `evidence.evidence_chain[${i}] evidence source_id`); + } + chain.claims.forEach((claim, ci) => { + check(claim.id, `evidence.evidence_chain[${i}].claims[${ci}].id`); + check(claim.decision_record_id, `evidence.evidence_chain[${i}].claims[${ci}].decision_record_id`); + }); + }); + + return errors.length > 0 ? combine(...errors) : pass(); +}; + +/** TC-SEM-02: every reference in the evidence chain resolves within the COTS. */ +export const validateChainResolution = (cots: Cots): Result => { + const errors: Result[] = []; + const sources = new Set(cots.inputs.source_artifacts.map((a) => a.id)); + const decisions = new Set(cots.decisions.map((d) => d.id)); + + cots.evidence.evidence_chain.forEach((chain, i) => { + if (chain.decision_id && !decisions.has(chain.decision_id)) { + errors.push( + fail("TC-SEM-02", `evidence.evidence_chain[${i}].decision_id '${chain.decision_id}' does not resolve to a DecisionRecord`) + ); + } + + const evidenceIds = new Set( + [...chain.supporting_evidence, ...chain.contradicting_evidence].map((e) => e.id) + ); + const claimIds = new Set(chain.claims.map((c) => c.id)); + + for (const item of [...chain.supporting_evidence, ...chain.contradicting_evidence]) { + if (!sources.has(item.source_id)) { + errors.push( + fail("TC-SEM-02", `evidence item '${item.id}' references unresolved source '${item.source_id}'`) + ); + } + if (!claimIds.has(item.claim_id)) { + errors.push( + fail("TC-SEM-02", `evidence item '${item.id}' references unresolved claim '${item.claim_id}'`) + ); + } + } + + chain.claims.forEach((claim) => { + for (const ref of [...claim.supporting_evidence, ...claim.contradicting_evidence]) { + if (!evidenceIds.has(ref)) { + errors.push( + fail("TC-SEM-02", `claim '${claim.id}' references unresolved evidence '${ref}'`) + ); + } + } + if (!decisions.has(claim.decision_record_id)) { + errors.push( + fail("TC-SEM-02", `claim '${claim.id}' references unresolved decision '${claim.decision_record_id}'`) + ); + } + }); + }); + + return errors.length > 0 ? combine(...errors) : pass(); +}; + +/** TC-SEM-03: timestamps are internally consistent (no evidence from the future). */ +export const validateTemporalOrdering = (cots: Cots): Result => { + const errors: Result[] = []; + const produced = ts(cots.produced_at); + + for (const a of cots.inputs.source_artifacts) { + if (ts(a.version.issued_at) > produced) { + errors.push( + fail("TC-SEM-03", `source '${a.id}' was issued after the COTS was produced (evidence from the future)`) + ); + } + } + + for (const d of cots.decisions) { + if (ts(d.created_at) < produced - 24 * 3600 * 1000) { + errors.push( + fail("TC-SEM-03", `decision '${d.id}' was created more than a day before the COTS was produced`) + ); + } + const v = (d as { validation?: { validated_at?: string } }).validation; + if (v?.validated_at !== undefined && ts(v.validated_at) < produced) { + errors.push( + fail("TC-SEM-03", `decision '${d.id}' was validated before the COTS was produced`) + ); + } + } + + return errors.length > 0 ? combine(...errors) : pass(); +}; + +/** + * TC-RC-01..05: recursive evidence chains per RFC-0002. + * Returns warnings/errors for the RFC-0002 fields. + */ +export const validateRecursiveChains = (cots: Cots): Result => { + const warnings: Result[] = []; + const errors: Result[] = []; + + const hasFields = + cots.evidence.chain_termination !== undefined || + cots.evidence.evidence_chain.some((c) => + [...c.supporting_evidence, ...c.contradicting_evidence].some((e) => e.source_type !== undefined) + ); + + if (!hasFields) { + warnings.push(warn("TC-RC-05", "pre-RFC-0002 COTS: no source_type or chain_termination fields (legacy)")); + return combine(...warnings); + } + + const termination = cots.evidence.chain_termination; + if (termination === undefined) { + warnings.push( + warn("TC-RC-05", "chain_termination is absent while source_type fields are present; treated as legacy") + ); + } else { + if (termination.policy === "bounded_recursion") { + if (termination.max_levels === undefined || termination.max_levels < 1) { + errors.push(fail("TC-RC-02", "bounded_recursion declared without a max_levels bound of at least 1")); + } else if (termination.max_levels > 1) { + warnings.push( + warn("TC-RC-02", `recursion bound is ${termination.max_levels}; the recommended bound is 1`) + ); + } + } else if (termination.policy === "unbounded_recursion") { + warnings.push(warn("TC-RC-03", "unbounded_recursion: chains may include any depth of model outputs")); + } + } + + const modelItems: string[] = []; + for (const chain of cots.evidence.evidence_chain) { + for (const item of [...chain.supporting_evidence, ...chain.contradicting_evidence]) { + if (item.source_type === "model-generated") { + modelItems.push(item.id); + } + } + } + + if (modelItems.length > 0) { + if (termination === undefined || termination.policy === "terminal") { + errors.push( + fail( + "TC-RC-04", + `chain termination policy '${termination?.policy ?? "terminal (default)"}' violated: model-generated evidence items ${modelItems.join(", ")}` + ) + ); + } else if (termination.policy === "bounded_recursion") { + warnings.push( + warn("TC-RC-01", `recursive evidence: ${modelItems.length} model-generated item(s); chain is recursive`) + ); + } else { + warnings.push( + warn("TC-RC-01", `recursive evidence: ${modelItems.length} model-generated item(s) under unbounded recursion`) + ); + } + } + + return combine(...errors, ...warnings); +}; diff --git a/src/fixtures/rc-bounded-high.json b/src/fixtures/rc-bounded-high.json new file mode 100644 index 0000000..e9052b3 --- /dev/null +++ b/src/fixtures/rc-bounded-high.json @@ -0,0 +1,162 @@ +{ + "cots_version": "0.1.0", + "cots_id": "urn:source-engineering:cots:00000000-0000-0000-0000-000000000001", + "produced_at": "2026-10-11T12:00:00Z", + "produced_by": { + "system_name": "test-fixture", + "system_version": "0.1.0" + }, + "inputs": { + "source_artifacts": [ + { + "id": "urn:source-engineering:source:contract-2026-001", + "origin": { + "system_name": "Acme Contracts", + "system_version": "1.0.0", + "configuration_hash": "0000000000000000000000000000000000000000000000000000000000000001" + }, + "version": { + "content_hash": "1111111111111111111111111111111111111111111111111111111111111111", + "issued_at": "2026-10-10T08:00:00Z" + }, + "authority": { + "asserted_by": "acme-contracts", + "trust_score": 0.95 + }, + "temporal_validity": { + "valid_from": "2026-10-10T08:00:00Z", + "valid_until": null + }, + "content_reference": { + "uri": "https://example.com/contracts/2026-001.pdf", + "media_type": "application/pdf" + } + } + ] + }, + "context": { + "model": { + "model_name": "claude-sonnet-4-5", + "model_version": "2026-09-15" + }, + "parameters": { + "temperature": 0.3, + "top_p": 0.95, + "prompt_cache_id": null, + "tool_budget": 8, + "retrieval_depth": 5 + }, + "constraints": [ + { + "id": "urn:source-engineering:constraint:jurisdiction-001", + "type": "jurisdiction", + "scope": "all_decisions", + "value": "US-NY" + } + ] + }, + "decisions": [ + { + "id": "urn:source-engineering:decision:00000000-0000-0000-0000-000000000001", + "decision_text": "The contract is a standard commercial services agreement with mutual indemnification, a 30-day termination notice, and a New York governing-law clause.", + "basis_id": "urn:source-engineering:basis:00000000-0000-0000-0000-000000000001", + "evidence_chain_ref": "urn:source-engineering:evidence-chain:00000000-0000-0000-0000-000000000001", + "validation_status": { + "status": "pending", + "last_changed_at": "2026-10-11T12:00:00Z", + "last_changed_by": "test-fixture" + }, + "responsible_authority": { + "authority_id": "claude-sonnet-4-5", + "authority_type": "automated" + }, + "audit_history": [ + { + "id": "urn:source-engineering:audit:00000000-0000-0000-0000-000000000001", + "action": "created", + "performed_by": "test-fixture", + "performed_at": "2026-10-11T12:00:00Z", + "notes": "Initial creation." + } + ], + "created_at": "2026-10-11T12:00:00Z" + } + ], + "evidence": { + "evidence_chain": [ + { + "decision_id": "urn:source-engineering:decision:00000000-0000-0000-0000-000000000001", + "supporting_evidence": [ + { + "id": "urn:source-engineering:evidence:00000000-0000-0000-0000-000000000001", + "claim_id": "urn:source-engineering:claim:00000000-0000-0000-0000-000000000001", + "source_id": "urn:source-engineering:source:contract-2026-001", + "transformation": { + "operation": "extract_clause", + "inputs": [ + "https://example.com/contracts/2026-001.pdf#section-7" + ], + "output": "Section 7.1: Each party shall indemnify and hold harmless...", + "recorded_at": "2026-10-11T12:00:00Z" + }, + "uncertainty": { + "calibrated_score": 0.92, + "basis": "Direct extraction from the section 7.1 text." + }, + "polarity": "supports", + "source_type": "human-authored" + } + ], + "contradicting_evidence": [ + { + "id": "urn:source-engineering:evidence:00000000-0000-0000-0000-000000000002", + "claim_id": "urn:source-engineering:claim:00000000-0000-0000-0000-000000000001", + "source_id": "urn:source-engineering:source:contract-2026-001", + "transformation": { + "operation": "search_clause", + "inputs": [ + "https://example.com/contracts/2026-001.pdf#section-12" + ], + "output": "Section 12.4: This agreement shall be governed by the laws of the State of Delaware.", + "recorded_at": "2026-10-11T12:00:00Z" + }, + "uncertainty": { + "calibrated_score": 0.78, + "basis": "Contradictory governing-law clause in section 12.4 (the model rejected it because section 14.1 is the prevailing clause)." + }, + "polarity": "contradicts" + } + ], + "claims": [ + { + "id": "urn:source-engineering:claim:00000000-0000-0000-0000-000000000001", + "decision_record_id": "urn:source-engineering:decision:00000000-0000-0000-0000-000000000001", + "claim_text": "The contract contains a mutual indemnification clause in section 7.1.", + "claim_type": "factual", + "supporting_evidence": [ + "urn:source-engineering:evidence:00000000-0000-0000-0000-000000000001" + ], + "contradicting_evidence": [ + "urn:source-engineering:evidence:00000000-0000-0000-0000-000000000002" + ] + } + ] + } + ], + "chain_termination": { + "policy": "bounded_recursion", + "max_levels": 3 + } + }, + "validation": { + "feedback_surfaces": [ + { + "id": "urn:source-engineering:surface:00000000-0000-0000-0000-000000000001", + "type": "appeal", + "target": "urn:source-engineering:decision:00000000-0000-0000-0000-000000000001", + "endpoint": "https://example.com/appeal/00000000-0000-0000-0000-000000000001", + "description": "File an appeal against this decision." + } + ] + } +} \ No newline at end of file diff --git a/src/fixtures/rc-bounded-ok.json b/src/fixtures/rc-bounded-ok.json new file mode 100644 index 0000000..4b6a3b9 --- /dev/null +++ b/src/fixtures/rc-bounded-ok.json @@ -0,0 +1,162 @@ +{ + "cots_version": "0.1.0", + "cots_id": "urn:source-engineering:cots:00000000-0000-0000-0000-000000000001", + "produced_at": "2026-10-11T12:00:00Z", + "produced_by": { + "system_name": "test-fixture", + "system_version": "0.1.0" + }, + "inputs": { + "source_artifacts": [ + { + "id": "urn:source-engineering:source:contract-2026-001", + "origin": { + "system_name": "Acme Contracts", + "system_version": "1.0.0", + "configuration_hash": "0000000000000000000000000000000000000000000000000000000000000001" + }, + "version": { + "content_hash": "1111111111111111111111111111111111111111111111111111111111111111", + "issued_at": "2026-10-10T08:00:00Z" + }, + "authority": { + "asserted_by": "acme-contracts", + "trust_score": 0.95 + }, + "temporal_validity": { + "valid_from": "2026-10-10T08:00:00Z", + "valid_until": null + }, + "content_reference": { + "uri": "https://example.com/contracts/2026-001.pdf", + "media_type": "application/pdf" + } + } + ] + }, + "context": { + "model": { + "model_name": "claude-sonnet-4-5", + "model_version": "2026-09-15" + }, + "parameters": { + "temperature": 0.3, + "top_p": 0.95, + "prompt_cache_id": null, + "tool_budget": 8, + "retrieval_depth": 5 + }, + "constraints": [ + { + "id": "urn:source-engineering:constraint:jurisdiction-001", + "type": "jurisdiction", + "scope": "all_decisions", + "value": "US-NY" + } + ] + }, + "decisions": [ + { + "id": "urn:source-engineering:decision:00000000-0000-0000-0000-000000000001", + "decision_text": "The contract is a standard commercial services agreement with mutual indemnification, a 30-day termination notice, and a New York governing-law clause.", + "basis_id": "urn:source-engineering:basis:00000000-0000-0000-0000-000000000001", + "evidence_chain_ref": "urn:source-engineering:evidence-chain:00000000-0000-0000-0000-000000000001", + "validation_status": { + "status": "pending", + "last_changed_at": "2026-10-11T12:00:00Z", + "last_changed_by": "test-fixture" + }, + "responsible_authority": { + "authority_id": "claude-sonnet-4-5", + "authority_type": "automated" + }, + "audit_history": [ + { + "id": "urn:source-engineering:audit:00000000-0000-0000-0000-000000000001", + "action": "created", + "performed_by": "test-fixture", + "performed_at": "2026-10-11T12:00:00Z", + "notes": "Initial creation." + } + ], + "created_at": "2026-10-11T12:00:00Z" + } + ], + "evidence": { + "evidence_chain": [ + { + "decision_id": "urn:source-engineering:decision:00000000-0000-0000-0000-000000000001", + "supporting_evidence": [ + { + "id": "urn:source-engineering:evidence:00000000-0000-0000-0000-000000000001", + "claim_id": "urn:source-engineering:claim:00000000-0000-0000-0000-000000000001", + "source_id": "urn:source-engineering:source:contract-2026-001", + "transformation": { + "operation": "extract_clause", + "inputs": [ + "https://example.com/contracts/2026-001.pdf#section-7" + ], + "output": "Section 7.1: Each party shall indemnify and hold harmless...", + "recorded_at": "2026-10-11T12:00:00Z" + }, + "uncertainty": { + "calibrated_score": 0.92, + "basis": "Direct extraction from the section 7.1 text." + }, + "polarity": "supports", + "source_type": "model-generated" + } + ], + "contradicting_evidence": [ + { + "id": "urn:source-engineering:evidence:00000000-0000-0000-0000-000000000002", + "claim_id": "urn:source-engineering:claim:00000000-0000-0000-0000-000000000001", + "source_id": "urn:source-engineering:source:contract-2026-001", + "transformation": { + "operation": "search_clause", + "inputs": [ + "https://example.com/contracts/2026-001.pdf#section-12" + ], + "output": "Section 12.4: This agreement shall be governed by the laws of the State of Delaware.", + "recorded_at": "2026-10-11T12:00:00Z" + }, + "uncertainty": { + "calibrated_score": 0.78, + "basis": "Contradictory governing-law clause in section 12.4 (the model rejected it because section 14.1 is the prevailing clause)." + }, + "polarity": "contradicts" + } + ], + "claims": [ + { + "id": "urn:source-engineering:claim:00000000-0000-0000-0000-000000000001", + "decision_record_id": "urn:source-engineering:decision:00000000-0000-0000-0000-000000000001", + "claim_text": "The contract contains a mutual indemnification clause in section 7.1.", + "claim_type": "factual", + "supporting_evidence": [ + "urn:source-engineering:evidence:00000000-0000-0000-0000-000000000001" + ], + "contradicting_evidence": [ + "urn:source-engineering:evidence:00000000-0000-0000-0000-000000000002" + ] + } + ] + } + ], + "chain_termination": { + "policy": "bounded_recursion", + "max_levels": 1 + } + }, + "validation": { + "feedback_surfaces": [ + { + "id": "urn:source-engineering:surface:00000000-0000-0000-0000-000000000001", + "type": "appeal", + "target": "urn:source-engineering:decision:00000000-0000-0000-0000-000000000001", + "endpoint": "https://example.com/appeal/00000000-0000-0000-0000-000000000001", + "description": "File an appeal against this decision." + } + ] + } +} \ No newline at end of file diff --git a/src/fixtures/rc-no-bound.json b/src/fixtures/rc-no-bound.json new file mode 100644 index 0000000..90bfd22 --- /dev/null +++ b/src/fixtures/rc-no-bound.json @@ -0,0 +1,160 @@ +{ + "cots_version": "0.1.0", + "cots_id": "urn:source-engineering:cots:00000000-0000-0000-0000-000000000001", + "produced_at": "2026-10-11T12:00:00Z", + "produced_by": { + "system_name": "test-fixture", + "system_version": "0.1.0" + }, + "inputs": { + "source_artifacts": [ + { + "id": "urn:source-engineering:source:contract-2026-001", + "origin": { + "system_name": "Acme Contracts", + "system_version": "1.0.0", + "configuration_hash": "0000000000000000000000000000000000000000000000000000000000000001" + }, + "version": { + "content_hash": "1111111111111111111111111111111111111111111111111111111111111111", + "issued_at": "2026-10-10T08:00:00Z" + }, + "authority": { + "asserted_by": "acme-contracts", + "trust_score": 0.95 + }, + "temporal_validity": { + "valid_from": "2026-10-10T08:00:00Z", + "valid_until": null + }, + "content_reference": { + "uri": "https://example.com/contracts/2026-001.pdf", + "media_type": "application/pdf" + } + } + ] + }, + "context": { + "model": { + "model_name": "claude-sonnet-4-5", + "model_version": "2026-09-15" + }, + "parameters": { + "temperature": 0.3, + "top_p": 0.95, + "prompt_cache_id": null, + "tool_budget": 8, + "retrieval_depth": 5 + }, + "constraints": [ + { + "id": "urn:source-engineering:constraint:jurisdiction-001", + "type": "jurisdiction", + "scope": "all_decisions", + "value": "US-NY" + } + ] + }, + "decisions": [ + { + "id": "urn:source-engineering:decision:00000000-0000-0000-0000-000000000001", + "decision_text": "The contract is a standard commercial services agreement with mutual indemnification, a 30-day termination notice, and a New York governing-law clause.", + "basis_id": "urn:source-engineering:basis:00000000-0000-0000-0000-000000000001", + "evidence_chain_ref": "urn:source-engineering:evidence-chain:00000000-0000-0000-0000-000000000001", + "validation_status": { + "status": "pending", + "last_changed_at": "2026-10-11T12:00:00Z", + "last_changed_by": "test-fixture" + }, + "responsible_authority": { + "authority_id": "claude-sonnet-4-5", + "authority_type": "automated" + }, + "audit_history": [ + { + "id": "urn:source-engineering:audit:00000000-0000-0000-0000-000000000001", + "action": "created", + "performed_by": "test-fixture", + "performed_at": "2026-10-11T12:00:00Z", + "notes": "Initial creation." + } + ], + "created_at": "2026-10-11T12:00:00Z" + } + ], + "evidence": { + "evidence_chain": [ + { + "decision_id": "urn:source-engineering:decision:00000000-0000-0000-0000-000000000001", + "supporting_evidence": [ + { + "id": "urn:source-engineering:evidence:00000000-0000-0000-0000-000000000001", + "claim_id": "urn:source-engineering:claim:00000000-0000-0000-0000-000000000001", + "source_id": "urn:source-engineering:source:contract-2026-001", + "transformation": { + "operation": "extract_clause", + "inputs": [ + "https://example.com/contracts/2026-001.pdf#section-7" + ], + "output": "Section 7.1: Each party shall indemnify and hold harmless...", + "recorded_at": "2026-10-11T12:00:00Z" + }, + "uncertainty": { + "calibrated_score": 0.92, + "basis": "Direct extraction from the section 7.1 text." + }, + "polarity": "supports" + } + ], + "contradicting_evidence": [ + { + "id": "urn:source-engineering:evidence:00000000-0000-0000-0000-000000000002", + "claim_id": "urn:source-engineering:claim:00000000-0000-0000-0000-000000000001", + "source_id": "urn:source-engineering:source:contract-2026-001", + "transformation": { + "operation": "search_clause", + "inputs": [ + "https://example.com/contracts/2026-001.pdf#section-12" + ], + "output": "Section 12.4: This agreement shall be governed by the laws of the State of Delaware.", + "recorded_at": "2026-10-11T12:00:00Z" + }, + "uncertainty": { + "calibrated_score": 0.78, + "basis": "Contradictory governing-law clause in section 12.4 (the model rejected it because section 14.1 is the prevailing clause)." + }, + "polarity": "contradicts" + } + ], + "claims": [ + { + "id": "urn:source-engineering:claim:00000000-0000-0000-0000-000000000001", + "decision_record_id": "urn:source-engineering:decision:00000000-0000-0000-0000-000000000001", + "claim_text": "The contract contains a mutual indemnification clause in section 7.1.", + "claim_type": "factual", + "supporting_evidence": [ + "urn:source-engineering:evidence:00000000-0000-0000-0000-000000000001" + ], + "contradicting_evidence": [ + "urn:source-engineering:evidence:00000000-0000-0000-0000-000000000002" + ] + } + ] + } + ], + "chain_termination": { + "policy": "bounded_recursion" + } + }, + "validation": { + "feedback_surfaces": [ + { + "id": "urn:source-engineering:surface:00000000-0000-0000-0000-000000000001", + "type": "appeal", + "target": "urn:source-engineering:decision:00000000-0000-0000-0000-000000000001", + "endpoint": "https://example.com/appeal/00000000-0000-0000-0000-000000000001", + "description": "File an appeal against this decision." + } + ] + } +} \ No newline at end of file diff --git a/src/fixtures/rc-terminal-violated.json b/src/fixtures/rc-terminal-violated.json new file mode 100644 index 0000000..113530d --- /dev/null +++ b/src/fixtures/rc-terminal-violated.json @@ -0,0 +1,161 @@ +{ + "cots_version": "0.1.0", + "cots_id": "urn:source-engineering:cots:00000000-0000-0000-0000-000000000001", + "produced_at": "2026-10-11T12:00:00Z", + "produced_by": { + "system_name": "test-fixture", + "system_version": "0.1.0" + }, + "inputs": { + "source_artifacts": [ + { + "id": "urn:source-engineering:source:contract-2026-001", + "origin": { + "system_name": "Acme Contracts", + "system_version": "1.0.0", + "configuration_hash": "0000000000000000000000000000000000000000000000000000000000000001" + }, + "version": { + "content_hash": "1111111111111111111111111111111111111111111111111111111111111111", + "issued_at": "2026-10-10T08:00:00Z" + }, + "authority": { + "asserted_by": "acme-contracts", + "trust_score": 0.95 + }, + "temporal_validity": { + "valid_from": "2026-10-10T08:00:00Z", + "valid_until": null + }, + "content_reference": { + "uri": "https://example.com/contracts/2026-001.pdf", + "media_type": "application/pdf" + } + } + ] + }, + "context": { + "model": { + "model_name": "claude-sonnet-4-5", + "model_version": "2026-09-15" + }, + "parameters": { + "temperature": 0.3, + "top_p": 0.95, + "prompt_cache_id": null, + "tool_budget": 8, + "retrieval_depth": 5 + }, + "constraints": [ + { + "id": "urn:source-engineering:constraint:jurisdiction-001", + "type": "jurisdiction", + "scope": "all_decisions", + "value": "US-NY" + } + ] + }, + "decisions": [ + { + "id": "urn:source-engineering:decision:00000000-0000-0000-0000-000000000001", + "decision_text": "The contract is a standard commercial services agreement with mutual indemnification, a 30-day termination notice, and a New York governing-law clause.", + "basis_id": "urn:source-engineering:basis:00000000-0000-0000-0000-000000000001", + "evidence_chain_ref": "urn:source-engineering:evidence-chain:00000000-0000-0000-0000-000000000001", + "validation_status": { + "status": "pending", + "last_changed_at": "2026-10-11T12:00:00Z", + "last_changed_by": "test-fixture" + }, + "responsible_authority": { + "authority_id": "claude-sonnet-4-5", + "authority_type": "automated" + }, + "audit_history": [ + { + "id": "urn:source-engineering:audit:00000000-0000-0000-0000-000000000001", + "action": "created", + "performed_by": "test-fixture", + "performed_at": "2026-10-11T12:00:00Z", + "notes": "Initial creation." + } + ], + "created_at": "2026-10-11T12:00:00Z" + } + ], + "evidence": { + "evidence_chain": [ + { + "decision_id": "urn:source-engineering:decision:00000000-0000-0000-0000-000000000001", + "supporting_evidence": [ + { + "id": "urn:source-engineering:evidence:00000000-0000-0000-0000-000000000001", + "claim_id": "urn:source-engineering:claim:00000000-0000-0000-0000-000000000001", + "source_id": "urn:source-engineering:source:contract-2026-001", + "transformation": { + "operation": "extract_clause", + "inputs": [ + "https://example.com/contracts/2026-001.pdf#section-7" + ], + "output": "Section 7.1: Each party shall indemnify and hold harmless...", + "recorded_at": "2026-10-11T12:00:00Z" + }, + "uncertainty": { + "calibrated_score": 0.92, + "basis": "Direct extraction from the section 7.1 text." + }, + "polarity": "supports", + "source_type": "model-generated" + } + ], + "contradicting_evidence": [ + { + "id": "urn:source-engineering:evidence:00000000-0000-0000-0000-000000000002", + "claim_id": "urn:source-engineering:claim:00000000-0000-0000-0000-000000000001", + "source_id": "urn:source-engineering:source:contract-2026-001", + "transformation": { + "operation": "search_clause", + "inputs": [ + "https://example.com/contracts/2026-001.pdf#section-12" + ], + "output": "Section 12.4: This agreement shall be governed by the laws of the State of Delaware.", + "recorded_at": "2026-10-11T12:00:00Z" + }, + "uncertainty": { + "calibrated_score": 0.78, + "basis": "Contradictory governing-law clause in section 12.4 (the model rejected it because section 14.1 is the prevailing clause)." + }, + "polarity": "contradicts" + } + ], + "claims": [ + { + "id": "urn:source-engineering:claim:00000000-0000-0000-0000-000000000001", + "decision_record_id": "urn:source-engineering:decision:00000000-0000-0000-0000-000000000001", + "claim_text": "The contract contains a mutual indemnification clause in section 7.1.", + "claim_type": "factual", + "supporting_evidence": [ + "urn:source-engineering:evidence:00000000-0000-0000-0000-000000000001" + ], + "contradicting_evidence": [ + "urn:source-engineering:evidence:00000000-0000-0000-0000-000000000002" + ] + } + ] + } + ], + "chain_termination": { + "policy": "terminal" + } + }, + "validation": { + "feedback_surfaces": [ + { + "id": "urn:source-engineering:surface:00000000-0000-0000-0000-000000000001", + "type": "appeal", + "target": "urn:source-engineering:decision:00000000-0000-0000-0000-000000000001", + "endpoint": "https://example.com/appeal/00000000-0000-0000-0000-000000000001", + "description": "File an appeal against this decision." + } + ] + } +} \ No newline at end of file diff --git a/src/fixtures/rc-unbounded.json b/src/fixtures/rc-unbounded.json new file mode 100644 index 0000000..0ee709b --- /dev/null +++ b/src/fixtures/rc-unbounded.json @@ -0,0 +1,161 @@ +{ + "cots_version": "0.1.0", + "cots_id": "urn:source-engineering:cots:00000000-0000-0000-0000-000000000001", + "produced_at": "2026-10-11T12:00:00Z", + "produced_by": { + "system_name": "test-fixture", + "system_version": "0.1.0" + }, + "inputs": { + "source_artifacts": [ + { + "id": "urn:source-engineering:source:contract-2026-001", + "origin": { + "system_name": "Acme Contracts", + "system_version": "1.0.0", + "configuration_hash": "0000000000000000000000000000000000000000000000000000000000000001" + }, + "version": { + "content_hash": "1111111111111111111111111111111111111111111111111111111111111111", + "issued_at": "2026-10-10T08:00:00Z" + }, + "authority": { + "asserted_by": "acme-contracts", + "trust_score": 0.95 + }, + "temporal_validity": { + "valid_from": "2026-10-10T08:00:00Z", + "valid_until": null + }, + "content_reference": { + "uri": "https://example.com/contracts/2026-001.pdf", + "media_type": "application/pdf" + } + } + ] + }, + "context": { + "model": { + "model_name": "claude-sonnet-4-5", + "model_version": "2026-09-15" + }, + "parameters": { + "temperature": 0.3, + "top_p": 0.95, + "prompt_cache_id": null, + "tool_budget": 8, + "retrieval_depth": 5 + }, + "constraints": [ + { + "id": "urn:source-engineering:constraint:jurisdiction-001", + "type": "jurisdiction", + "scope": "all_decisions", + "value": "US-NY" + } + ] + }, + "decisions": [ + { + "id": "urn:source-engineering:decision:00000000-0000-0000-0000-000000000001", + "decision_text": "The contract is a standard commercial services agreement with mutual indemnification, a 30-day termination notice, and a New York governing-law clause.", + "basis_id": "urn:source-engineering:basis:00000000-0000-0000-0000-000000000001", + "evidence_chain_ref": "urn:source-engineering:evidence-chain:00000000-0000-0000-0000-000000000001", + "validation_status": { + "status": "pending", + "last_changed_at": "2026-10-11T12:00:00Z", + "last_changed_by": "test-fixture" + }, + "responsible_authority": { + "authority_id": "claude-sonnet-4-5", + "authority_type": "automated" + }, + "audit_history": [ + { + "id": "urn:source-engineering:audit:00000000-0000-0000-0000-000000000001", + "action": "created", + "performed_by": "test-fixture", + "performed_at": "2026-10-11T12:00:00Z", + "notes": "Initial creation." + } + ], + "created_at": "2026-10-11T12:00:00Z" + } + ], + "evidence": { + "evidence_chain": [ + { + "decision_id": "urn:source-engineering:decision:00000000-0000-0000-0000-000000000001", + "supporting_evidence": [ + { + "id": "urn:source-engineering:evidence:00000000-0000-0000-0000-000000000001", + "claim_id": "urn:source-engineering:claim:00000000-0000-0000-0000-000000000001", + "source_id": "urn:source-engineering:source:contract-2026-001", + "transformation": { + "operation": "extract_clause", + "inputs": [ + "https://example.com/contracts/2026-001.pdf#section-7" + ], + "output": "Section 7.1: Each party shall indemnify and hold harmless...", + "recorded_at": "2026-10-11T12:00:00Z" + }, + "uncertainty": { + "calibrated_score": 0.92, + "basis": "Direct extraction from the section 7.1 text." + }, + "polarity": "supports", + "source_type": "human-authored" + } + ], + "contradicting_evidence": [ + { + "id": "urn:source-engineering:evidence:00000000-0000-0000-0000-000000000002", + "claim_id": "urn:source-engineering:claim:00000000-0000-0000-0000-000000000001", + "source_id": "urn:source-engineering:source:contract-2026-001", + "transformation": { + "operation": "search_clause", + "inputs": [ + "https://example.com/contracts/2026-001.pdf#section-12" + ], + "output": "Section 12.4: This agreement shall be governed by the laws of the State of Delaware.", + "recorded_at": "2026-10-11T12:00:00Z" + }, + "uncertainty": { + "calibrated_score": 0.78, + "basis": "Contradictory governing-law clause in section 12.4 (the model rejected it because section 14.1 is the prevailing clause)." + }, + "polarity": "contradicts" + } + ], + "claims": [ + { + "id": "urn:source-engineering:claim:00000000-0000-0000-0000-000000000001", + "decision_record_id": "urn:source-engineering:decision:00000000-0000-0000-0000-000000000001", + "claim_text": "The contract contains a mutual indemnification clause in section 7.1.", + "claim_type": "factual", + "supporting_evidence": [ + "urn:source-engineering:evidence:00000000-0000-0000-0000-000000000001" + ], + "contradicting_evidence": [ + "urn:source-engineering:evidence:00000000-0000-0000-0000-000000000002" + ] + } + ] + } + ], + "chain_termination": { + "policy": "unbounded_recursion" + } + }, + "validation": { + "feedback_surfaces": [ + { + "id": "urn:source-engineering:surface:00000000-0000-0000-0000-000000000001", + "type": "appeal", + "target": "urn:source-engineering:decision:00000000-0000-0000-0000-000000000001", + "endpoint": "https://example.com/appeal/00000000-0000-0000-0000-000000000001", + "description": "File an appeal against this decision." + } + ] + } +} \ No newline at end of file diff --git a/src/fixtures/sem-bad-urn.json b/src/fixtures/sem-bad-urn.json new file mode 100644 index 0000000..1a07bf0 --- /dev/null +++ b/src/fixtures/sem-bad-urn.json @@ -0,0 +1,157 @@ +{ + "cots_version": "0.1.0", + "cots_id": "not-a-urn", + "produced_at": "2026-10-11T12:00:00Z", + "produced_by": { + "system_name": "test-fixture", + "system_version": "0.1.0" + }, + "inputs": { + "source_artifacts": [ + { + "id": "urn:source-engineering:source:contract-2026-001", + "origin": { + "system_name": "Acme Contracts", + "system_version": "1.0.0", + "configuration_hash": "0000000000000000000000000000000000000000000000000000000000000001" + }, + "version": { + "content_hash": "1111111111111111111111111111111111111111111111111111111111111111", + "issued_at": "2026-10-10T08:00:00Z" + }, + "authority": { + "asserted_by": "acme-contracts", + "trust_score": 0.95 + }, + "temporal_validity": { + "valid_from": "2026-10-10T08:00:00Z", + "valid_until": null + }, + "content_reference": { + "uri": "https://example.com/contracts/2026-001.pdf", + "media_type": "application/pdf" + } + } + ] + }, + "context": { + "model": { + "model_name": "claude-sonnet-4-5", + "model_version": "2026-09-15" + }, + "parameters": { + "temperature": 0.3, + "top_p": 0.95, + "prompt_cache_id": null, + "tool_budget": 8, + "retrieval_depth": 5 + }, + "constraints": [ + { + "id": "urn:source-engineering:constraint:jurisdiction-001", + "type": "jurisdiction", + "scope": "all_decisions", + "value": "US-NY" + } + ] + }, + "decisions": [ + { + "id": "urn:source-engineering:decision:00000000-0000-0000-0000-000000000001", + "decision_text": "The contract is a standard commercial services agreement with mutual indemnification, a 30-day termination notice, and a New York governing-law clause.", + "basis_id": "urn:source-engineering:basis:00000000-0000-0000-0000-000000000001", + "evidence_chain_ref": "urn:source-engineering:evidence-chain:00000000-0000-0000-0000-000000000001", + "validation_status": { + "status": "pending", + "last_changed_at": "2026-10-11T12:00:00Z", + "last_changed_by": "test-fixture" + }, + "responsible_authority": { + "authority_id": "claude-sonnet-4-5", + "authority_type": "automated" + }, + "audit_history": [ + { + "id": "urn:source-engineering:audit:00000000-0000-0000-0000-000000000001", + "action": "created", + "performed_by": "test-fixture", + "performed_at": "2026-10-11T12:00:00Z", + "notes": "Initial creation." + } + ], + "created_at": "2026-10-11T12:00:00Z" + } + ], + "evidence": { + "evidence_chain": [ + { + "decision_id": "urn:source-engineering:decision:00000000-0000-0000-0000-000000000001", + "supporting_evidence": [ + { + "id": "urn:source-engineering:evidence:00000000-0000-0000-0000-000000000001", + "claim_id": "urn:source-engineering:claim:00000000-0000-0000-0000-000000000001", + "source_id": "urn:source-engineering:source:contract-2026-001", + "transformation": { + "operation": "extract_clause", + "inputs": [ + "https://example.com/contracts/2026-001.pdf#section-7" + ], + "output": "Section 7.1: Each party shall indemnify and hold harmless...", + "recorded_at": "2026-10-11T12:00:00Z" + }, + "uncertainty": { + "calibrated_score": 0.92, + "basis": "Direct extraction from the section 7.1 text." + }, + "polarity": "supports" + } + ], + "contradicting_evidence": [ + { + "id": "urn:source-engineering:evidence:00000000-0000-0000-0000-000000000002", + "claim_id": "urn:source-engineering:claim:00000000-0000-0000-0000-000000000001", + "source_id": "urn:source-engineering:source:contract-2026-001", + "transformation": { + "operation": "search_clause", + "inputs": [ + "https://example.com/contracts/2026-001.pdf#section-12" + ], + "output": "Section 12.4: This agreement shall be governed by the laws of the State of Delaware.", + "recorded_at": "2026-10-11T12:00:00Z" + }, + "uncertainty": { + "calibrated_score": 0.78, + "basis": "Contradictory governing-law clause in section 12.4 (the model rejected it because section 14.1 is the prevailing clause)." + }, + "polarity": "contradicts" + } + ], + "claims": [ + { + "id": "urn:source-engineering:claim:00000000-0000-0000-0000-000000000001", + "decision_record_id": "urn:source-engineering:decision:00000000-0000-0000-0000-000000000001", + "claim_text": "The contract contains a mutual indemnification clause in section 7.1.", + "claim_type": "factual", + "supporting_evidence": [ + "urn:source-engineering:evidence:00000000-0000-0000-0000-000000000001" + ], + "contradicting_evidence": [ + "urn:source-engineering:evidence:00000000-0000-0000-0000-000000000002" + ] + } + ] + } + ] + }, + "validation": { + "feedback_surfaces": [ + { + "id": "urn:source-engineering:surface:00000000-0000-0000-0000-000000000001", + "type": "appeal", + "target": "urn:source-engineering:decision:00000000-0000-0000-0000-000000000001", + "endpoint": "https://example.com/appeal/00000000-0000-0000-0000-000000000001", + "description": "File an appeal against this decision." + } + ] + } +} \ No newline at end of file diff --git a/src/fixtures/sem-dangling-source.json b/src/fixtures/sem-dangling-source.json new file mode 100644 index 0000000..6811444 --- /dev/null +++ b/src/fixtures/sem-dangling-source.json @@ -0,0 +1,157 @@ +{ + "cots_version": "0.1.0", + "cots_id": "urn:source-engineering:cots:00000000-0000-0000-0000-000000000001", + "produced_at": "2026-10-11T12:00:00Z", + "produced_by": { + "system_name": "test-fixture", + "system_version": "0.1.0" + }, + "inputs": { + "source_artifacts": [ + { + "id": "urn:source-engineering:source:contract-2026-001", + "origin": { + "system_name": "Acme Contracts", + "system_version": "1.0.0", + "configuration_hash": "0000000000000000000000000000000000000000000000000000000000000001" + }, + "version": { + "content_hash": "1111111111111111111111111111111111111111111111111111111111111111", + "issued_at": "2026-10-10T08:00:00Z" + }, + "authority": { + "asserted_by": "acme-contracts", + "trust_score": 0.95 + }, + "temporal_validity": { + "valid_from": "2026-10-10T08:00:00Z", + "valid_until": null + }, + "content_reference": { + "uri": "https://example.com/contracts/2026-001.pdf", + "media_type": "application/pdf" + } + } + ] + }, + "context": { + "model": { + "model_name": "claude-sonnet-4-5", + "model_version": "2026-09-15" + }, + "parameters": { + "temperature": 0.3, + "top_p": 0.95, + "prompt_cache_id": null, + "tool_budget": 8, + "retrieval_depth": 5 + }, + "constraints": [ + { + "id": "urn:source-engineering:constraint:jurisdiction-001", + "type": "jurisdiction", + "scope": "all_decisions", + "value": "US-NY" + } + ] + }, + "decisions": [ + { + "id": "urn:source-engineering:decision:00000000-0000-0000-0000-000000000001", + "decision_text": "The contract is a standard commercial services agreement with mutual indemnification, a 30-day termination notice, and a New York governing-law clause.", + "basis_id": "urn:source-engineering:basis:00000000-0000-0000-0000-000000000001", + "evidence_chain_ref": "urn:source-engineering:evidence-chain:00000000-0000-0000-0000-000000000001", + "validation_status": { + "status": "pending", + "last_changed_at": "2026-10-11T12:00:00Z", + "last_changed_by": "test-fixture" + }, + "responsible_authority": { + "authority_id": "claude-sonnet-4-5", + "authority_type": "automated" + }, + "audit_history": [ + { + "id": "urn:source-engineering:audit:00000000-0000-0000-0000-000000000001", + "action": "created", + "performed_by": "test-fixture", + "performed_at": "2026-10-11T12:00:00Z", + "notes": "Initial creation." + } + ], + "created_at": "2026-10-11T12:00:00Z" + } + ], + "evidence": { + "evidence_chain": [ + { + "decision_id": "urn:source-engineering:decision:00000000-0000-0000-0000-000000000001", + "supporting_evidence": [ + { + "id": "urn:source-engineering:evidence:00000000-0000-0000-0000-000000000001", + "claim_id": "urn:source-engineering:claim:00000000-0000-0000-0000-000000000001", + "source_id": "urn:source-engineering:source:does-not-exist", + "transformation": { + "operation": "extract_clause", + "inputs": [ + "https://example.com/contracts/2026-001.pdf#section-7" + ], + "output": "Section 7.1: Each party shall indemnify and hold harmless...", + "recorded_at": "2026-10-11T12:00:00Z" + }, + "uncertainty": { + "calibrated_score": 0.92, + "basis": "Direct extraction from the section 7.1 text." + }, + "polarity": "supports" + } + ], + "contradicting_evidence": [ + { + "id": "urn:source-engineering:evidence:00000000-0000-0000-0000-000000000002", + "claim_id": "urn:source-engineering:claim:00000000-0000-0000-0000-000000000001", + "source_id": "urn:source-engineering:source:contract-2026-001", + "transformation": { + "operation": "search_clause", + "inputs": [ + "https://example.com/contracts/2026-001.pdf#section-12" + ], + "output": "Section 12.4: This agreement shall be governed by the laws of the State of Delaware.", + "recorded_at": "2026-10-11T12:00:00Z" + }, + "uncertainty": { + "calibrated_score": 0.78, + "basis": "Contradictory governing-law clause in section 12.4 (the model rejected it because section 14.1 is the prevailing clause)." + }, + "polarity": "contradicts" + } + ], + "claims": [ + { + "id": "urn:source-engineering:claim:00000000-0000-0000-0000-000000000001", + "decision_record_id": "urn:source-engineering:decision:00000000-0000-0000-0000-000000000001", + "claim_text": "The contract contains a mutual indemnification clause in section 7.1.", + "claim_type": "factual", + "supporting_evidence": [ + "urn:source-engineering:evidence:00000000-0000-0000-0000-000000000001" + ], + "contradicting_evidence": [ + "urn:source-engineering:evidence:00000000-0000-0000-0000-000000000002" + ] + } + ] + } + ] + }, + "validation": { + "feedback_surfaces": [ + { + "id": "urn:source-engineering:surface:00000000-0000-0000-0000-000000000001", + "type": "appeal", + "target": "urn:source-engineering:decision:00000000-0000-0000-0000-000000000001", + "endpoint": "https://example.com/appeal/00000000-0000-0000-0000-000000000001", + "description": "File an appeal against this decision." + } + ] + } +} \ No newline at end of file diff --git a/src/fixtures/sem-future-evidence.json b/src/fixtures/sem-future-evidence.json new file mode 100644 index 0000000..453ac8f --- /dev/null +++ b/src/fixtures/sem-future-evidence.json @@ -0,0 +1,157 @@ +{ + "cots_version": "0.1.0", + "cots_id": "urn:source-engineering:cots:00000000-0000-0000-0000-000000000001", + "produced_at": "2026-10-11T12:00:00Z", + "produced_by": { + "system_name": "test-fixture", + "system_version": "0.1.0" + }, + "inputs": { + "source_artifacts": [ + { + "id": "urn:source-engineering:source:contract-2026-001", + "origin": { + "system_name": "Acme Contracts", + "system_version": "1.0.0", + "configuration_hash": "0000000000000000000000000000000000000000000000000000000000000001" + }, + "version": { + "content_hash": "1111111111111111111111111111111111111111111111111111111111111111", + "issued_at": "2027-01-01T00:00:00Z" + }, + "authority": { + "asserted_by": "acme-contracts", + "trust_score": 0.95 + }, + "temporal_validity": { + "valid_from": "2026-10-10T08:00:00Z", + "valid_until": null + }, + "content_reference": { + "uri": "https://example.com/contracts/2026-001.pdf", + "media_type": "application/pdf" + } + } + ] + }, + "context": { + "model": { + "model_name": "claude-sonnet-4-5", + "model_version": "2026-09-15" + }, + "parameters": { + "temperature": 0.3, + "top_p": 0.95, + "prompt_cache_id": null, + "tool_budget": 8, + "retrieval_depth": 5 + }, + "constraints": [ + { + "id": "urn:source-engineering:constraint:jurisdiction-001", + "type": "jurisdiction", + "scope": "all_decisions", + "value": "US-NY" + } + ] + }, + "decisions": [ + { + "id": "urn:source-engineering:decision:00000000-0000-0000-0000-000000000001", + "decision_text": "The contract is a standard commercial services agreement with mutual indemnification, a 30-day termination notice, and a New York governing-law clause.", + "basis_id": "urn:source-engineering:basis:00000000-0000-0000-0000-000000000001", + "evidence_chain_ref": "urn:source-engineering:evidence-chain:00000000-0000-0000-0000-000000000001", + "validation_status": { + "status": "pending", + "last_changed_at": "2026-10-11T12:00:00Z", + "last_changed_by": "test-fixture" + }, + "responsible_authority": { + "authority_id": "claude-sonnet-4-5", + "authority_type": "automated" + }, + "audit_history": [ + { + "id": "urn:source-engineering:audit:00000000-0000-0000-0000-000000000001", + "action": "created", + "performed_by": "test-fixture", + "performed_at": "2026-10-11T12:00:00Z", + "notes": "Initial creation." + } + ], + "created_at": "2026-10-11T12:00:00Z" + } + ], + "evidence": { + "evidence_chain": [ + { + "decision_id": "urn:source-engineering:decision:00000000-0000-0000-0000-000000000001", + "supporting_evidence": [ + { + "id": "urn:source-engineering:evidence:00000000-0000-0000-0000-000000000001", + "claim_id": "urn:source-engineering:claim:00000000-0000-0000-0000-000000000001", + "source_id": "urn:source-engineering:source:contract-2026-001", + "transformation": { + "operation": "extract_clause", + "inputs": [ + "https://example.com/contracts/2026-001.pdf#section-7" + ], + "output": "Section 7.1: Each party shall indemnify and hold harmless...", + "recorded_at": "2026-10-11T12:00:00Z" + }, + "uncertainty": { + "calibrated_score": 0.92, + "basis": "Direct extraction from the section 7.1 text." + }, + "polarity": "supports" + } + ], + "contradicting_evidence": [ + { + "id": "urn:source-engineering:evidence:00000000-0000-0000-0000-000000000002", + "claim_id": "urn:source-engineering:claim:00000000-0000-0000-0000-000000000001", + "source_id": "urn:source-engineering:source:contract-2026-001", + "transformation": { + "operation": "search_clause", + "inputs": [ + "https://example.com/contracts/2026-001.pdf#section-12" + ], + "output": "Section 12.4: This agreement shall be governed by the laws of the State of Delaware.", + "recorded_at": "2026-10-11T12:00:00Z" + }, + "uncertainty": { + "calibrated_score": 0.78, + "basis": "Contradictory governing-law clause in section 12.4 (the model rejected it because section 14.1 is the prevailing clause)." + }, + "polarity": "contradicts" + } + ], + "claims": [ + { + "id": "urn:source-engineering:claim:00000000-0000-0000-0000-000000000001", + "decision_record_id": "urn:source-engineering:decision:00000000-0000-0000-0000-000000000001", + "claim_text": "The contract contains a mutual indemnification clause in section 7.1.", + "claim_type": "factual", + "supporting_evidence": [ + "urn:source-engineering:evidence:00000000-0000-0000-0000-000000000001" + ], + "contradicting_evidence": [ + "urn:source-engineering:evidence:00000000-0000-0000-0000-000000000002" + ] + } + ] + } + ] + }, + "validation": { + "feedback_surfaces": [ + { + "id": "urn:source-engineering:surface:00000000-0000-0000-0000-000000000001", + "type": "appeal", + "target": "urn:source-engineering:decision:00000000-0000-0000-0000-000000000001", + "endpoint": "https://example.com/appeal/00000000-0000-0000-0000-000000000001", + "description": "File an appeal against this decision." + } + ] + } +} \ No newline at end of file diff --git a/src/runner.ts b/src/runner.ts index 1c8b11d..fa966e6 100644 --- a/src/runner.ts +++ b/src/runner.ts @@ -17,6 +17,7 @@ import { validateDecisionBasis, validateCounterevidence } from "./contracts/deci import { validateAuditHistory, validateAuditHistoryOrdered } from "./contracts/lifecycle-integrity.js"; import { validateFiveParts, validateFeedbackSurfaces } from "./contracts/five-parts.js"; import { validateLayer2Records } from "./contracts/layer2-integrity.js"; +import { validateUrnGrammar, validateChainResolution, validateTemporalOrdering, validateRecursiveChains } from "./contracts/semantic-integrity.js"; export const run = (cots: Cots): Result => { return combine( @@ -30,7 +31,11 @@ export const run = (cots: Cots): Result => { validateAuditHistoryOrdered(cots), validateFiveParts(cots), validateFeedbackSurfaces(cots), - validateLayer2Records(cots) + validateLayer2Records(cots), + validateUrnGrammar(cots), + validateChainResolution(cots), + validateTemporalOrdering(cots), + validateRecursiveChains(cots) ); }; diff --git a/src/types.ts b/src/types.ts index dc410f5..daf4cab 100644 --- a/src/types.ts +++ b/src/types.ts @@ -167,7 +167,18 @@ export interface AuditEntry { notes: string; } +export type SourceType = "human-authored" | "human-curated" | "model-generated" | "mixed"; + +export type ChainTerminationPolicy = "terminal" | "bounded_recursion" | "unbounded_recursion"; + +export interface ChainTermination { + policy: ChainTerminationPolicy; + max_levels?: number; +} + export interface Evidence { + /** RFC-0002 (COTS schema v1.2.0). Optional; absent on pre-RFC-0002 COTSs. */ + chain_termination?: ChainTermination; evidence_chain: EvidenceChainEntry[]; } @@ -194,6 +205,8 @@ export interface EvidenceItem { transformation: Transformation; uncertainty: Uncertainty; polarity: Polarity; + /** RFC-0002 (COTS schema v1.2.0). Optional; absent on pre-RFC-0002 COTSs. */ + source_type?: SourceType; } export interface Transformation { diff --git a/tests/semantic.test.ts b/tests/semantic.test.ts new file mode 100644 index 0000000..c4f16ac --- /dev/null +++ b/tests/semantic.test.ts @@ -0,0 +1,105 @@ +/** + * Semantic integrity test cases (TC-SEM-01..03) and recursive chain test + * cases (TC-RC-01..05), derived from the canonical test cases in the + * Source Engineering specifications and RFC-0002. + * + * See: https://github.com/Source-Engineering/source-engineering-specifications/blob/main/compatibility-test-suite.md + */ + +import { describe, it, expect } from "vitest"; +import { readFileSync } from "node:fs"; +import { resolve } from "node:path"; +import { fileURLToPath } from "node:url"; +import { dirname, join } from "node:path"; +import { + validateUrnGrammar, + validateChainResolution, + validateTemporalOrdering, + validateRecursiveChains, +} from "../src/contracts/semantic-integrity.js"; +import { run } from "../src/runner.js"; +import type { Cots } from "../src/types.js"; + +const __dirname = dirname(fileURLToPath(import.meta.url)); +const FIXTURES = join(__dirname, "..", "src", "fixtures"); + +const load = (name: string): Cots => + JSON.parse(readFileSync(resolve(FIXTURES, name), "utf-8")) as Cots; + +describe("Semantic integrity", () => { + it("TC-SEM-01: well-formed URNs pass", () => { + const result = validateUrnGrammar(load("conformant.json")); + expect(result.errors).toHaveLength(0); + }); + + it("TC-SEM-01: a malformed URN fails", () => { + const result = validateUrnGrammar(load("sem-bad-urn.json")); + expect(result.errors.some((e) => e.test_id === "TC-SEM-01")).toBe(true); + }); + + it("TC-SEM-02: resolving references pass", () => { + const result = validateChainResolution(load("conformant.json")); + expect(result.errors).toHaveLength(0); + }); + + it("TC-SEM-02: a dangling source reference fails", () => { + const result = validateChainResolution(load("sem-dangling-source.json")); + expect(result.errors.some((e) => e.test_id === "TC-SEM-02")).toBe(true); + }); + + it("TC-SEM-03: consistent timestamps pass", () => { + const result = validateTemporalOrdering(load("conformant.json")); + expect(result.errors).toHaveLength(0); + }); + + it("TC-SEM-03: evidence from the future fails", () => { + const result = validateTemporalOrdering(load("sem-future-evidence.json")); + expect(result.errors.some((e) => e.test_id === "TC-SEM-03")).toBe(true); + }); +}); + +describe("Recursive evidence chains (RFC-0002)", () => { + it("TC-RC-04: a terminal COTS with a model-generated evidence item fails", () => { + const result = validateRecursiveChains(load("rc-terminal-violated.json")); + expect(result.errors.some((e) => e.test_id === "TC-RC-04")).toBe(true); + }); + + it("TC-RC-01/02: bounded recursion with a model-generated item passes with warnings", () => { + const result = validateRecursiveChains(load("rc-bounded-ok.json")); + expect(result.status).toBe("pass"); + expect(result.errors).toHaveLength(0); + expect(result.warnings.some((w) => w.test_id === "TC-RC-01")).toBe(true); + }); + + it("TC-RC-02: a recursion bound greater than 1 warns", () => { + const result = validateRecursiveChains(load("rc-bounded-high.json")); + expect(result.warnings.some((w) => w.test_id === "TC-RC-02")).toBe(true); + }); + + it("TC-RC-02: bounded recursion without a bound fails", () => { + const result = validateRecursiveChains(load("rc-no-bound.json")); + expect(result.errors.some((e) => e.test_id === "TC-RC-02")).toBe(true); + }); + + it("TC-RC-03: unbounded recursion warns", () => { + const result = validateRecursiveChains(load("rc-unbounded.json")); + expect(result.warnings.some((w) => w.test_id === "TC-RC-03")).toBe(true); + }); + + it("TC-RC-05: a legacy COTS without RFC-0002 fields warns as legacy", () => { + const result = validateRecursiveChains(load("conformant.json")); + expect(result.status).toBe("pass"); + expect(result.warnings.some((w) => w.test_id === "TC-RC-05")).toBe(true); + }); + + it("the full runner fails the terminal-policy violation end-to-end", () => { + const result = run(load("rc-terminal-violated.json")); + expect(result.status).toBe("fail"); + expect(result.errors.some((e) => e.test_id === "TC-RC-04")).toBe(true); + }); + + it("the full runner fails a malformed URN end-to-end", () => { + const result = run(load("sem-bad-urn.json")); + expect(result.status).toBe("fail"); + }); +});