Found while implementing #547 against main 1d4c0f841b1d11f5f8e822d60263aa55cd77ded5; deferred under the requested issue-by-issue implementation discipline.
Problem
A failed final identity pass cannot currently tell its caller which captured entry failed or whether it observed identity drift versus being unable to inspect that entry. The caller can truthfully name the final-validation phase and read root, but cannot supply the narrower repair location or claim a proven race.
Evidence
core/trust_roots.py::IdentityBoundReadSession.finish has distinct branches for root metadata drift, changed directory names, changed captured entry identity and an OSError from requested.lstat(). All use ValueError; the latter is rethrown as "path changed while it was read" even though the observation was a failed inspection. The local directory/requested value is not carried in typed failure data. read_bytes likewise reports oversized files, non-regular/multiply-linked inputs and several identity failures through ValueError.
The #547 regression fixtures perform a real post-read file change and separately inject a generic ValueError/OSError at final validation. All reject authority. #547 now labels them snapshot_validation_failed and requests a fresh coherent read; it deliberately does not classify exception prose as drift. A later successful run establishes a new answer, not the cause of the earlier failure.
Bounded follow-up
Preserve typed facts at the reader branch that actually knows them: validation phase, root-relative directory/entry, and observed identity mismatch versus unavailable inspection. Start with final validation; inventory every shared reader consumer before propagating the optional detail. Do not introduce a new verifier verdict or widen frozen control unions. Raw exception messages may contain sensitive paths/context and are not a public diagnostic payload.
Acceptance
Related: #547 provides the immediate actionable phase/source/bound route; #548 is a distinct authorization-overlay prerequisite diagnosis. This deeper reader refinement is deferred and is not evidence that the intermittent failures in either issue were caused by a race.
Found while implementing #547 against main
1d4c0f841b1d11f5f8e822d60263aa55cd77ded5; deferred under the requested issue-by-issue implementation discipline.Problem
A failed final identity pass cannot currently tell its caller which captured entry failed or whether it observed identity drift versus being unable to inspect that entry. The caller can truthfully name the final-validation phase and read root, but cannot supply the narrower repair location or claim a proven race.
Evidence
core/trust_roots.py::IdentityBoundReadSession.finishhas distinct branches for root metadata drift, changed directory names, changed captured entry identity and anOSErrorfromrequested.lstat(). All useValueError; the latter is rethrown as "path changed while it was read" even though the observation was a failed inspection. The localdirectory/requestedvalue is not carried in typed failure data.read_byteslikewise reports oversized files, non-regular/multiply-linked inputs and several identity failures throughValueError.The #547 regression fixtures perform a real post-read file change and separately inject a generic
ValueError/OSErrorat final validation. All reject authority. #547 now labels themsnapshot_validation_failedand requests a fresh coherent read; it deliberately does not classify exception prose as drift. A later successful run establishes a new answer, not the cause of the earlier failure.Bounded follow-up
Preserve typed facts at the reader branch that actually knows them: validation phase, root-relative directory/entry, and observed identity mismatch versus unavailable inspection. Start with final validation; inventory every shared reader consumer before propagating the optional detail. Do not introduce a new verifier verdict or widen frozen control unions. Raw exception messages may contain sensitive paths/context and are not a public diagnostic payload.
Acceptance
Related: #547 provides the immediate actionable phase/source/bound route; #548 is a distinct authorization-overlay prerequisite diagnosis. This deeper reader refinement is deferred and is not evidence that the intermittent failures in either issue were caused by a race.