From 84df9b9c81170ef3bbbfd6572f0a379c8a09d582 Mon Sep 17 00:00:00 2001 From: Vibe Nuage Agent Date: Fri, 30 Oct 2026 10:40:54 +0000 Subject: [PATCH] feat(conformance): implement TC-L2-01..06 Layer 2 validators, types, and fixtures per RFC-0001 Co-authored-by: emmanuel-a-otchere --- .gitignore | 3 + src/contracts/layer2-integrity.ts | 94 +++++++++++ src/fixtures/layer2-bad-decision.json | 163 +++++++++++++++++++ src/fixtures/layer2-bad-flag-type-full.json | 169 ++++++++++++++++++++ src/fixtures/layer2-bad-timestamp.json | 163 +++++++++++++++++++ src/fixtures/layer2-conformant.json | 169 ++++++++++++++++++++ src/fixtures/layer2-mismatch.json | 169 ++++++++++++++++++++ src/runner.ts | 4 +- src/types.ts | 23 +++ tests/layer2.test.ts | 66 ++++++++ 10 files changed, 1022 insertions(+), 1 deletion(-) create mode 100644 .gitignore create mode 100644 src/contracts/layer2-integrity.ts create mode 100644 src/fixtures/layer2-bad-decision.json create mode 100644 src/fixtures/layer2-bad-flag-type-full.json create mode 100644 src/fixtures/layer2-bad-timestamp.json create mode 100644 src/fixtures/layer2-conformant.json create mode 100644 src/fixtures/layer2-mismatch.json create mode 100644 tests/layer2.test.ts diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3ff38cc --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +node_modules/ +dist/ +.vite/ diff --git a/src/contracts/layer2-integrity.ts b/src/contracts/layer2-integrity.ts new file mode 100644 index 0000000..c1f24aa --- /dev/null +++ b/src/contracts/layer2-integrity.ts @@ -0,0 +1,94 @@ +/** + * Layer 2 Validation record validators. Implements TC-L2-01..06. + * + * See: https://github.com/Source-Engineering/source-engineering-specifications/blob/main/compatibility-test-suite.md + * See: RFC-0001 (v0.2 floor: Layer 2). + */ + +import type { Cots } from "../types.js"; +import { type Result, combine, fail, pass } from "../result.js"; + +const FLAG_TYPES = [ + "drift-detected", + "segment-signal-divergence", + "parameter-set-out-of-range", + "temporal-validity-expired", +] as const; + +const DECISIONS_ON_VALIDATION = ["passed", "flagged", "escalated"] as const; + +const isIso8601 = (value: string): boolean => !Number.isNaN(Date.parse(value)); + +/** + * TC-L2-01 .. TC-L2-06. Validates every DecisionRecord's optional + * `validation` field. The field is optional: a missing field passes + * (TC-L2-06, the producer has not floored Layer 2). + */ +export const validateLayer2Records = (cots: Cots): Result => { + const errors: Result[] = []; + + cots.decisions.forEach((decision, index) => { + const v = decision.validation; + if (v === undefined) { + return; + } + + if (!Array.isArray(v.flags)) { + errors.push(fail("TC-L2-01", `decisions[${index}].validation.flags is not an array`)); + return; + } + + v.flags.forEach((flag, fIndex) => { + if (!(FLAG_TYPES as readonly string[]).includes(flag.flag_type)) { + errors.push( + fail( + "TC-L2-02", + `decisions[${index}].validation.flags[${fIndex}].flag_type '${flag.flag_type}' is not in the declared enum` + ) + ); + } + if (!isIso8601(flag.detected_at)) { + errors.push( + fail( + "TC-L2-01", + `decisions[${index}].validation.flags[${fIndex}].detected_at is not a valid ISO-8601 timestamp` + ) + ); + } + }); + + if (!(DECISIONS_ON_VALIDATION as readonly string[]).includes(v.decision_on_validation)) { + errors.push( + fail( + "TC-L2-03", + `decisions[${index}].validation.decision_on_validation '${v.decision_on_validation}' is not in the declared enum (passed, flagged, escalated). Layer 2 does not reject; it flags.` + ) + ); + } else if ( + (v.flags.length > 0 && v.decision_on_validation === "passed") || + (v.flags.length === 0 && v.decision_on_validation !== "passed") + ) { + errors.push( + fail( + "TC-L2-04", + `decisions[${index}].validation: decision_on_validation '${v.decision_on_validation}' is inconsistent with ${v.flags.length} flags` + ) + ); + } + + if (!isIso8601(v.validated_at)) { + errors.push( + fail("TC-L2-05", `decisions[${index}].validation.validated_at is not a valid ISO-8601 timestamp`) + ); + } + + if (typeof v.validated_by !== "string" || v.validated_by.length === 0) { + errors.push(fail("TC-L2-01", `decisions[${index}].validation.validated_by is missing or empty`)); + } + }); + + if (errors.length === 0) { + return pass(); + } + return combine(...errors); +}; diff --git a/src/fixtures/layer2-bad-decision.json b/src/fixtures/layer2-bad-decision.json new file mode 100644 index 0000000..c0d26fd --- /dev/null +++ b/src/fixtures/layer2-bad-decision.json @@ -0,0 +1,163 @@ +{ + "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", + "validation": { + "flags": [], + "decision_on_validation": "rejected", + "validated_at": "2026-10-11T12:00:00Z", + "validated_by": "layer2-runtime-validation" + } + } + ], + "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/layer2-bad-flag-type-full.json b/src/fixtures/layer2-bad-flag-type-full.json new file mode 100644 index 0000000..435c031 --- /dev/null +++ b/src/fixtures/layer2-bad-flag-type-full.json @@ -0,0 +1,169 @@ +{ + "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", + "validation": { + "flags": [ + { + "flag_type": "made-up-flag", + "message": "A flag type outside the declared enum.", + "detected_at": "2026-10-11T12:00:00Z" + } + ], + "decision_on_validation": "flagged", + "validated_at": "2026-10-11T12:00:00Z", + "validated_by": "layer2-runtime-validation" + } + } + ], + "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/layer2-bad-timestamp.json b/src/fixtures/layer2-bad-timestamp.json new file mode 100644 index 0000000..4cd38fe --- /dev/null +++ b/src/fixtures/layer2-bad-timestamp.json @@ -0,0 +1,163 @@ +{ + "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", + "validation": { + "flags": [], + "decision_on_validation": "passed", + "validated_at": "yesterday", + "validated_by": "layer2-runtime-validation" + } + } + ], + "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/layer2-conformant.json b/src/fixtures/layer2-conformant.json new file mode 100644 index 0000000..5e306c3 --- /dev/null +++ b/src/fixtures/layer2-conformant.json @@ -0,0 +1,169 @@ +{ + "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", + "validation": { + "flags": [ + { + "flag_type": "drift-detected", + "message": "Divergence 0.14 above threshold 0.10.", + "detected_at": "2026-10-11T12:00:00Z" + } + ], + "decision_on_validation": "flagged", + "validated_at": "2026-10-11T12:00:00Z", + "validated_by": "layer2-runtime-validation" + } + } + ], + "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/layer2-mismatch.json b/src/fixtures/layer2-mismatch.json new file mode 100644 index 0000000..3a486de --- /dev/null +++ b/src/fixtures/layer2-mismatch.json @@ -0,0 +1,169 @@ +{ + "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", + "validation": { + "flags": [ + { + "flag_type": "drift-detected", + "message": "x", + "detected_at": "2026-10-11T12:00:00Z" + } + ], + "decision_on_validation": "passed", + "validated_at": "2026-10-11T12:00:00Z", + "validated_by": "layer2-runtime-validation" + } + } + ], + "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 8d3b800..1c8b11d 100644 --- a/src/runner.ts +++ b/src/runner.ts @@ -16,6 +16,7 @@ import { validateEvidenceChain } from "./contracts/evidential-integrity.js"; import { validateDecisionBasis, validateCounterevidence } from "./contracts/decision-integrity.js"; import { validateAuditHistory, validateAuditHistoryOrdered } from "./contracts/lifecycle-integrity.js"; import { validateFiveParts, validateFeedbackSurfaces } from "./contracts/five-parts.js"; +import { validateLayer2Records } from "./contracts/layer2-integrity.js"; export const run = (cots: Cots): Result => { return combine( @@ -28,7 +29,8 @@ export const run = (cots: Cots): Result => { validateAuditHistory(cots), validateAuditHistoryOrdered(cots), validateFiveParts(cots), - validateFeedbackSurfaces(cots) + validateFeedbackSurfaces(cots), + validateLayer2Records(cots) ); }; diff --git a/src/types.ts b/src/types.ts index e21adaa..dc410f5 100644 --- a/src/types.ts +++ b/src/types.ts @@ -123,6 +123,29 @@ export interface DecisionRecord { responsible_authority: ResponsibleAuthority; audit_history: AuditEntry[]; created_at: ISO8601; + /** Layer 2 Validation record (COTS schema v1.1.0, RFC-0001). Optional. */ + validation?: Layer2Validation; +} + +export type FlagType = + | "drift-detected" + | "segment-signal-divergence" + | "parameter-set-out-of-range" + | "temporal-validity-expired"; + +export type DecisionOnValidation = "passed" | "flagged" | "escalated"; + +export interface Layer2ValidationFlag { + flag_type: FlagType; + message: string; + detected_at: ISO8601; +} + +export interface Layer2Validation { + flags: Layer2ValidationFlag[]; + decision_on_validation: DecisionOnValidation; + validated_at: ISO8601; + validated_by: string; } export interface ValidationStatus { diff --git a/tests/layer2.test.ts b/tests/layer2.test.ts new file mode 100644 index 0000000..2f0dcfd --- /dev/null +++ b/tests/layer2.test.ts @@ -0,0 +1,66 @@ +/** + * Layer 2 Validation record test cases (TC-L2-01..06), derived from the + * canonical test cases in the Source Engineering specifications. + * + * 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 { validateLayer2Records } from "../src/contracts/layer2-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 => { + const p = resolve(FIXTURES, name); + return JSON.parse(readFileSync(p, "utf-8")) as Cots; +}; + +describe("Layer 2 Validation records", () => { + it("TC-L2-01: a well-formed Validation record passes", () => { + const result = validateLayer2Records(load("layer2-conformant.json")); + expect(result.errors).toHaveLength(0); + }); + + it("TC-L2-02: an unknown flag_type fails", () => { + const result = validateLayer2Records(load("layer2-bad-flag-type-full.json")); + expect(result.errors.some((e) => e.test_id === "TC-L2-02")).toBe(true); + }); + + it("TC-L2-03: an unknown decision_on_validation fails (Layer 2 does not reject)", () => { + const result = validateLayer2Records(load("layer2-bad-decision.json")); + expect(result.errors.some((e) => e.test_id === "TC-L2-03")).toBe(true); + }); + + it("TC-L2-04: flags inconsistent with decision_on_validation fail", () => { + const result = validateLayer2Records(load("layer2-mismatch.json")); + expect(result.errors.some((e) => e.test_id === "TC-L2-04")).toBe(true); + }); + + it("TC-L2-05: a malformed validated_at fails", () => { + const result = validateLayer2Records(load("layer2-bad-timestamp.json")); + expect(result.errors.some((e) => e.test_id === "TC-L2-05")).toBe(true); + }); + + it("TC-L2-06: a v1.0.0 COTS without the validation field passes", () => { + const result = validateLayer2Records(load("conformant.json")); + expect(result.errors).toHaveLength(0); + expect(result.warnings).toHaveLength(0); + }); + + it("the full runner passes the conformant Layer 2 fixture end-to-end", () => { + const result = run(load("layer2-conformant.json")); + expect(result.errors).toHaveLength(0); + }); + + it("the full runner fails the flag/decision mismatch end-to-end", () => { + const result = run(load("layer2-mismatch.json")); + expect(result.errors.some((e) => e.test_id === "TC-L2-04")).toBe(true); + }); +});