## [2026-06-12T13:31:19.942Z]
Introduced computeExpectedPairs and computeSourceFingerprint as the read-side foundation for the verdict lock. The engine needs one shared definition of a node's subject-file set so the future check and fill stages can compare stored verdicts against the expected pair universe without duplicating subject-file derivation logic across multiple files. The binary-file catalogue and the child carve-out helper (getChildMappingExclusions) were moved to this module because pairs.ts is the canonical site where subject files are assembled — keeping these concerns co-located prevents the three sites that previously each had their own notion of a node's effective files from diverging again. Verification units are now computed from effective aspects crossed with each aspect's declared review granularity (per: node vs per: file), with scope.files filtering applied before the unit split.
## [2026-06-12T13:40:41.017Z]
Removed an unreachable defensive kind === 'aggregate' guard that caused a TypeScript type-narrowing error. The aggregate case is fully handled by the isAggregateAspect check immediately above, so the kind cast to 'llm' | 'deterministic' is always valid at the guard's location. TypeScript correctly flagged the comparison as having no overlap.
## [2026-06-12T13:53:48.024Z]
The scope.files filter in computeExpectedPairs was discarding files that evaluateFileWhen reported as unreadable (e.g. due to EACCES) without any signal to the caller. A file that cannot be read is excluded from the reviewer's subject set, which means if it was the only match, the aspect gets zero pairs — a vacuously green result despite a real gap in coverage. The system is specified fail-closed: every unreadable file under a content filter must surface as a blocking error. Changed computeExpectedPairs to return PairComputation {pairs, unreadable} instead of a bare array. Unreadable candidates are recorded in an UnreadableSubject[] with nodePath, aspectId, path, and the OS error reason. Callers are documented as required to surface non-empty unreadable as a blocking error. Deduplication prevents duplicate records for the same (nodePath, aspectId, path) triple.
## [2026-06-12T14:01:37.947Z]
Documented two TOCTOU/missing-file asymmetries in JSDoc: computeSourceFingerprint is fail-closed (a file vanishing between enumeration and hashing propagates a thrown error rather than silently producing the same fingerprint), while computeExpectedPairs treats files that vanish before scope evaluation as silently absent — only content-filter read failures surface via the explicit unreadable channel. Added a perf-tracking note above the getChildMappingExclusions call explaining the current O(nodes-squared) cost and the precompute-index escape hatch if check latency grows at scale.
## [2026-06-13T05:33:52.313Z]
A narrowed subject set — a single-file run or a filtered selection — now drives both the unit that is reviewed and the boundary of what that review may observe, so a scoped check sees exactly the files in scope and nothing wider. The set of nodes whose rule applicability cannot be computed is now exposed to callers so that cleanup of recorded results never deletes the verdicts of those nodes, preserving prior verification when effective rules are momentarily uncomputable.
## [2026-06-13T07:01:58.961Z]
A source file listed in a component's file set must be readable. A listed file that could not be read was previously dropped from the review surface without complaint, so a rule could be judged against a shrunken or even empty set of files and pass without ever inspecting the code it was meant to guard — a silent false approval, which is exactly what the verification cache exists to prevent. The contract is now fail-closed: every file in the review set is checked for readability, any file that cannot be read is reported as a blocking error and removed from the set so no verdict is ever produced over content the reviewer never saw, and the per-component source fingerprint refuses to hash an unreadable file, raising a typed error rather than folding a partial set. Anything written into a component's file list must be present and readable, or the run stays red until it is.
## [2026-06-13T08:46:13.059Z]
When a content-based file filter cannot evaluate a mapped file because the file exceeds the content-scan size limit, the file is now surfaced as a blocking error instead of being silently excluded from the review set. A silently dropped subject can turn an enforced rule into a vacuous pass — a green light over code no reviewer read. The diagnostic is phrased accurately for the size case (the file is readable but too large to scan, so 'could not read' would mislead) and points the author at concrete remedies: split the file under the limit, narrow the filter, or drop the file from the mapping.
