fix(review): Apply sees the file under every spelling and rechecks only its choice

Apply now finds the legacy rows of the same physical file however its root
is reached. The roots are real paths while the monitor links under the
directory it watches, so a symlinked or junctioned root, subst or a mapped
drive used to hide the monitor's row and Apply added a second, differently
linked row. Same-name rows are resolved to their physical identity
(normcase(realpath), else samefile); the dependency fingerprint covers all
of them, two rows refuse the choice, one row is enriched under the
ingester's compatible-only rules, and a same-name row the request did not
classify is a changed association.

Apply re-verifies only what the decision depends on: the bytes are still
ambiguous and still offer the chosen candidate. A resolver change that
rewords the reason or reorders candidates leaves pending items applicable.
The resolver version is one constant in prompt_pair_resolver, shared by
ingestion provenance and Needs review.

The transaction's byte check receives the re-hashed digest (or the body's
for Leave unresolved), every Apply refusal branch is tested with zero
writes, and the root listing and realpath calls run on a worker thread.
This commit is contained in:
Vito Sansevero
2026-10-22 17:29:15 -07:00
parent c4ac90e9ec
commit 32cc071d07
7 changed files with 550 additions and 81 deletions
+6
View File
@@ -39,6 +39,12 @@ from .pair_execution import ExecutionEvidenceError, read_pair_execution
_LOGGER = logging.getLogger("prompt_manager.prompt_pair_resolver")
# The ingestion resolver's version, recorded in pair-link provenance and on
# Needs review items. Bump it when a change would resolve the same metadata
# otherwise; a pending item stays applicable while its chosen candidate is
# still offered by the captured bytes.
RESOLVER_VERSION = 1
@dataclass(frozen=True)
class OutputAdapter:
+2 -4
View File
@@ -31,18 +31,16 @@ try:
from ..database.review_schema import MAX_CANDIDATES, canonical_evidence
from .media_capture import CaptureCapacity, CaptureError
from .prompt_inspector import MetadataReadError, read_inspection_file
from .prompt_pair_resolver import resolve_prompt_pair
from .prompt_pair_resolver import RESOLVER_VERSION, resolve_prompt_pair
except ImportError: # imported with the repo root on sys.path
from database.library_commands import LibraryCommandError
from database.review_schema import MAX_CANDIDATES, canonical_evidence
from utils.media_capture import CaptureCapacity, CaptureError
from utils.prompt_inspector import MetadataReadError, read_inspection_file
from utils.prompt_pair_resolver import resolve_prompt_pair
from utils.prompt_pair_resolver import RESOLVER_VERSION, resolve_prompt_pair
logger = logging.getLogger("prompt_manager.review_capture")
# The ingestion resolver's provenance version (``database.pair_ingestion``).
RESOLVER_VERSION = 1
# Capture scope label: review captures are never handed to a client.
REVIEW_SCOPE = "needs-review"
# Nested ambiguous stages are flattened at most this deep.