Class: KairosMcp::DriftDetection::CorrespondenceChecker
- Inherits:
-
Object
- Object
- KairosMcp::DriftDetection::CorrespondenceChecker
- Defined in:
- lib/kairos_mcp/drift_detection/correspondence_checker.rb
Overview
CorrespondenceChecker β INV-A detection floor (Cycle 1, toward by-construction).
Checks whether a live L0/L1 artifact still corresponds to its *current recorded provenance*: the content digest stored for that artifact at the head of the constitutive record (the hash chain). This is detection only βit surfaces divergence; it does not prevent edits or gate writes. Those are later cycles (single-source enforcement, record-as-gate).
Provenance is rooted in the hash chain, not in the SQLite knowledge_meta cache: INV-A names the chain head as the non-editable anchor. The chain is therefore the single source consulted here; the meta table (when present) is a derived view and is intentionally not used for the comparison.
The digest is computed over the *raw file content* (frontmatter included), matching exactly how it was recorded on create/update (a verbatim write, no normalization). Comparing the parsed/stripped body would never match.
Defined Under Namespace
Classes: Result
Class Method Summary collapse
-
.check_l1(name:, md_file_path:, storage_backend: nil) ⇒ Result
Check an L1 knowledge artifact against its recorded provenance.
Class Method Details
.check_l1(name:, md_file_path:, storage_backend: nil) ⇒ Result
Check an L1 knowledge artifact against its recorded provenance.
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 |
# File 'lib/kairos_mcp/drift_detection/correspondence_checker.rb', line 55 def check_l1(name:, md_file_path:, storage_backend: nil) unless md_file_path && File.file?(md_file_path) # Relied upon but absent β a missing artifact is itself a # non-correspondence under INV-A (the expected set is recorded). return Result.new( status: :missing_artifact, name: name, active_digest: nil, recorded_digest: nil, message: "L1 '#{name}': artifact missing at the point of reliance" ) end active = Digest::SHA256.hexdigest(File.read(md_file_path)) recorded = recorded_digest_for(name, storage_backend) if recorded.nil? return Result.new( status: :missing_record, name: name, active_digest: active, recorded_digest: nil, message: "L1 '#{name}': live artifact has no recorded provenance on the chain" ) end if active == recorded Result.new( status: :match, name: name, active_digest: active, recorded_digest: recorded, message: nil ) else Result.new( status: :mismatch, name: name, active_digest: active, recorded_digest: recorded, message: "L1 '#{name}': live content diverged from recorded provenance " \ "(active #{short(active)} β recorded #{short(recorded)})" ) end rescue StandardError => e Result.new( status: :error, name: name, active_digest: nil, recorded_digest: nil, message: "L1 '#{name}': correspondence check could not complete: #{e.}" ) end |