diff --git a/src/contracts/layer3-integrity.ts b/src/contracts/layer3-integrity.ts new file mode 100644 index 0000000..75ab6f4 --- /dev/null +++ b/src/contracts/layer3-integrity.ts @@ -0,0 +1,93 @@ +/** + * Layer 3 GuardrailDecision validators. Implements TC-L3-01..07. + * + * See: https://github.com/Source-Engineering/source-engineering-specifications/blob/main/compatibility-test-suite.md + * See: RFC-0003 (v0.4 floor: Layer 3 business guardrails). + */ + +import type { Cots } from "../types.js"; +import { type Result, combine, fail, pass } from "../result.js"; + +const OUTCOMES = ["accepted", "rejected", "annotated", "escalated"] as const; + +const isIso8601 = (value: string): boolean => !Number.isNaN(Date.parse(value)); + +/** + * TC-L3-01..07. Validates every DecisionRecord's optional `guardrail` + * field. The field is optional: a missing field passes (TC-L3-07). + */ +export const validateGuardrailDecisions = (cots: Cots): Result => { + const errors: Result[] = []; + + cots.decisions.forEach((decision, index) => { + const g = decision.guardrail; + if (g === undefined) { + return; + } + + if (!(OUTCOMES as readonly string[]).includes(g.decision)) { + errors.push( + fail( + "TC-L3-02", + `decisions[${index}].guardrail.decision '${g.decision}' is not in the declared enum (accepted, rejected, annotated, escalated)` + ) + ); + } + + if (g.decision === "rejected" && g.binding !== true) { + errors.push( + fail( + "TC-L3-03", + `decisions[${index}].guardrail: decision is 'rejected' but binding is false. Only a binding rule can reject.` + ) + ); + } + + if (g.decision === "annotated" && g.binding === true) { + errors.push( + fail( + "TC-L3-04", + `decisions[${index}].guardrail: decision is 'annotated' but binding is true. An advisory rule annotates; it does not carry binding force.` + ) + ); + } + + if (typeof g.rationale !== "string" || g.rationale.trim().length === 0) { + errors.push( + fail("TC-L3-05", `decisions[${index}].guardrail.rationale is missing or empty. Every GuardrailDecision is recorded with its rationale.`) + ); + } + + if (!isIso8601(g.evaluated_at)) { + errors.push( + fail("TC-L3-01", `decisions[${index}].guardrail.evaluated_at is not a valid ISO-8601 timestamp`) + ); + } + + if (!/^urn:source-engineering:rule:/.test(g.rule_id)) { + errors.push( + fail("TC-L3-01", `decisions[${index}].guardrail.rule_id '${g.rule_id}' is not a well-formed rule URN (urn:source-engineering:rule:...)`) + ); + } + + const status = decision.validation_status.status; + if (g.decision === "rejected" && status !== "superseded") { + errors.push( + fail( + "TC-L3-06", + `decisions[${index}].guardrail: decision is 'rejected' but validation_status.status is '${status}'; it MUST be 'superseded'` + ) + ); + } + if (g.decision === "escalated" && status !== "appealed") { + errors.push( + fail( + "TC-L3-06", + `decisions[${index}].guardrail: decision is 'escalated' but validation_status.status is '${status}'; it MUST be 'appealed'` + ) + ); + } + }); + + return errors.length > 0 ? combine(...errors) : pass(); +}; diff --git a/src/fixtures/l3-accepted.json b/src/fixtures/l3-accepted.json new file mode 100644 index 0000000..668fbb8 --- /dev/null +++ b/src/fixtures/l3-accepted.json @@ -0,0 +1,165 @@ +{ + "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", + "guardrail": { + "decision": "accepted", + "rule_id": "urn:source-engineering:rule:ny-jurisdiction-001", + "rationale": "A rationale in domain terms.", + "binding": false, + "evaluated_at": "2026-10-30T12:00:00Z", + "evaluated_by": "layer3-business-guardrails" + } + } + ], + "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/l3-annotated-binding.json b/src/fixtures/l3-annotated-binding.json new file mode 100644 index 0000000..fbfd16d --- /dev/null +++ b/src/fixtures/l3-annotated-binding.json @@ -0,0 +1,165 @@ +{ + "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", + "guardrail": { + "decision": "annotated", + "rule_id": "urn:source-engineering:rule:ny-jurisdiction-001", + "rationale": "A rationale in domain terms.", + "binding": true, + "evaluated_at": "2026-10-30T12:00:00Z", + "evaluated_by": "layer3-business-guardrails" + } + } + ], + "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/l3-annotated.json b/src/fixtures/l3-annotated.json new file mode 100644 index 0000000..b3c0696 --- /dev/null +++ b/src/fixtures/l3-annotated.json @@ -0,0 +1,165 @@ +{ + "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", + "guardrail": { + "decision": "annotated", + "rule_id": "urn:source-engineering:rule:ny-jurisdiction-001", + "rationale": "A rationale in domain terms.", + "binding": false, + "evaluated_at": "2026-10-30T12:00:00Z", + "evaluated_by": "layer3-business-guardrails" + } + } + ], + "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/l3-bad-decision.json b/src/fixtures/l3-bad-decision.json new file mode 100644 index 0000000..4ffe5b5 --- /dev/null +++ b/src/fixtures/l3-bad-decision.json @@ -0,0 +1,165 @@ +{ + "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", + "guardrail": { + "decision": "blocked", + "rule_id": "urn:source-engineering:rule:ny-jurisdiction-001", + "rationale": "A rationale in domain terms.", + "binding": true, + "evaluated_at": "2026-10-30T12:00:00Z", + "evaluated_by": "layer3-business-guardrails" + } + } + ], + "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/l3-escalated.json b/src/fixtures/l3-escalated.json new file mode 100644 index 0000000..df29488 --- /dev/null +++ b/src/fixtures/l3-escalated.json @@ -0,0 +1,165 @@ +{ + "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": "appealed", + "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", + "guardrail": { + "decision": "escalated", + "rule_id": "urn:source-engineering:rule:ny-jurisdiction-001", + "rationale": "A rationale in domain terms.", + "binding": true, + "evaluated_at": "2026-10-30T12:00:00Z", + "evaluated_by": "layer3-business-guardrails" + } + } + ], + "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/l3-no-rationale.json b/src/fixtures/l3-no-rationale.json new file mode 100644 index 0000000..f5fbe3f --- /dev/null +++ b/src/fixtures/l3-no-rationale.json @@ -0,0 +1,165 @@ +{ + "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": "superseded", + "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", + "guardrail": { + "decision": "rejected", + "rule_id": "urn:source-engineering:rule:ny-jurisdiction-001", + "rationale": "", + "binding": true, + "evaluated_at": "2026-10-30T12:00:00Z", + "evaluated_by": "layer3-business-guardrails" + } + } + ], + "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/l3-rejected-not-binding.json b/src/fixtures/l3-rejected-not-binding.json new file mode 100644 index 0000000..988de5d --- /dev/null +++ b/src/fixtures/l3-rejected-not-binding.json @@ -0,0 +1,165 @@ +{ + "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": "superseded", + "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", + "guardrail": { + "decision": "rejected", + "rule_id": "urn:source-engineering:rule:ny-jurisdiction-001", + "rationale": "A rationale in domain terms.", + "binding": false, + "evaluated_at": "2026-10-30T12:00:00Z", + "evaluated_by": "layer3-business-guardrails" + } + } + ], + "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/l3-rejected.json b/src/fixtures/l3-rejected.json new file mode 100644 index 0000000..8917fda --- /dev/null +++ b/src/fixtures/l3-rejected.json @@ -0,0 +1,165 @@ +{ + "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": "superseded", + "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", + "guardrail": { + "decision": "rejected", + "rule_id": "urn:source-engineering:rule:ny-jurisdiction-001", + "rationale": "A rationale in domain terms.", + "binding": true, + "evaluated_at": "2026-10-30T12:00:00Z", + "evaluated_by": "layer3-business-guardrails" + } + } + ], + "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/l3-status-inconsistent.json b/src/fixtures/l3-status-inconsistent.json new file mode 100644 index 0000000..9b3efd0 --- /dev/null +++ b/src/fixtures/l3-status-inconsistent.json @@ -0,0 +1,165 @@ +{ + "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", + "guardrail": { + "decision": "rejected", + "rule_id": "urn:source-engineering:rule:ny-jurisdiction-001", + "rationale": "A rationale in domain terms.", + "binding": true, + "evaluated_at": "2026-10-30T12:00:00Z", + "evaluated_by": "layer3-business-guardrails" + } + } + ], + "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 fa966e6..741ad36 100644 --- a/src/runner.ts +++ b/src/runner.ts @@ -18,6 +18,7 @@ import { validateAuditHistory, validateAuditHistoryOrdered } from "./contracts/l 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"; +import { validateGuardrailDecisions } from "./contracts/layer3-integrity.js"; export const run = (cots: Cots): Result => { return combine( @@ -35,7 +36,8 @@ export const run = (cots: Cots): Result => { validateUrnGrammar(cots), validateChainResolution(cots), validateTemporalOrdering(cots), - validateRecursiveChains(cots) + validateRecursiveChains(cots), + validateGuardrailDecisions(cots) ); }; diff --git a/src/types.ts b/src/types.ts index daf4cab..0971028 100644 --- a/src/types.ts +++ b/src/types.ts @@ -125,6 +125,19 @@ export interface DecisionRecord { created_at: ISO8601; /** Layer 2 Validation record (COTS schema v1.1.0, RFC-0001). Optional. */ validation?: Layer2Validation; + /** Layer 3 GuardrailDecision (COTS schema v1.3.0, RFC-0003). Optional. */ + guardrail?: GuardrailDecision; +} + +export type GuardrailOutcome = "accepted" | "rejected" | "annotated" | "escalated"; + +export interface GuardrailDecision { + decision: GuardrailOutcome; + rule_id: URI; + rationale: string; + binding: boolean; + evaluated_at: ISO8601; + evaluated_by?: string; } export type FlagType = diff --git a/tests/layer3.test.ts b/tests/layer3.test.ts new file mode 100644 index 0000000..4d62a27 --- /dev/null +++ b/tests/layer3.test.ts @@ -0,0 +1,68 @@ +/** + * Layer 3 GuardrailDecision test cases (TC-L3-01..07), derived from the + * canonical test cases in the Source Engineering specifications and RFC-0003. + */ + +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 { validateGuardrailDecisions } from "../src/contracts/layer3-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("Layer 3 GuardrailDecisions", () => { + it("TC-L3-01: well-formed guardrail records pass (accepted, rejected, annotated, escalated)", () => { + for (const f of ["l3-accepted.json", "l3-rejected.json", "l3-annotated.json", "l3-escalated.json"]) { + const result = validateGuardrailDecisions(load(f)); + expect(result.errors, f).toHaveLength(0); + } + }); + + it("TC-L3-02: an unknown guardrail decision fails", () => { + const result = validateGuardrailDecisions(load("l3-bad-decision.json")); + expect(result.errors.some((e) => e.test_id === "TC-L3-02")).toBe(true); + }); + + it("TC-L3-03: a rejection that is not binding fails", () => { + const result = validateGuardrailDecisions(load("l3-rejected-not-binding.json")); + expect(result.errors.some((e) => e.test_id === "TC-L3-03")).toBe(true); + }); + + it("TC-L3-04: an annotation marked binding fails", () => { + const result = validateGuardrailDecisions(load("l3-annotated-binding.json")); + expect(result.errors.some((e) => e.test_id === "TC-L3-04")).toBe(true); + }); + + it("TC-L3-05: an empty rationale fails", () => { + const result = validateGuardrailDecisions(load("l3-no-rationale.json")); + expect(result.errors.some((e) => e.test_id === "TC-L3-05")).toBe(true); + }); + + it("TC-L3-06: a rejected decision whose status is not superseded fails", () => { + const result = validateGuardrailDecisions(load("l3-status-inconsistent.json")); + expect(result.errors.some((e) => e.test_id === "TC-L3-06")).toBe(true); + }); + + it("TC-L3-07: a v1.2.0 COTS without the guardrail field passes", () => { + const result = validateGuardrailDecisions(load("conformant.json")); + expect(result.errors).toHaveLength(0); + }); + + it("the full runner fails the non-binding rejection end-to-end", () => { + const result = run(load("l3-rejected-not-binding.json")); + expect(result.status).toBe("fail"); + }); + + it("the full runner passes the well-formed rejection end-to-end", () => { + const result = run(load("l3-rejected.json")); + expect(result.errors.filter((e) => e.test_id.startsWith("TC-L3"))).toHaveLength(0); + }); +});