This is an automated email from the ASF dual-hosted git repository.

terrymanu pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/shardingsphere.git


The following commit(s) were added to refs/heads/master by this push:
     new b2f81b2ff64 Improve review evidence validation and coverage checks 
(#40169)
b2f81b2ff64 is described below

commit b2f81b2ff64ce3d283540dbbc590e2d1ca151823
Author: Liang Zhang <[email protected]>
AuthorDate: Tue Oct 13 13:50:41 2026 +0800

    Improve review evidence validation and coverage checks (#40169)
---
 .codex/harness/agents/cases.toml                   |  40 ++
 .codex/skills/review-pr/SKILL.md                   |  28 ++
 .../review-pr/references/review-corrections.md     |   6 +-
 .codex/skills/review-pr/scripts/review_ledger.py   | 379 ++++++++++++++++--
 .../skills/review-pr/tests/test_review_ledger.py   | 432 +++++++++++++++++++--
 5 files changed, 813 insertions(+), 72 deletions(-)

diff --git a/.codex/harness/agents/cases.toml b/.codex/harness/agents/cases.toml
index 7331a946dc4..8a14e86d4f1 100644
--- a/.codex/harness/agents/cases.toml
+++ b/.codex/harness/agents/cases.toml
@@ -3555,3 +3555,43 @@ max_summary_chars = 107
 critical = true
 policy_binding_profile = "gen-ut"
 policy_assertions = [{source = "gen-ut", statements = ["Edit only 
`<ResolvedTestFileSet>` under `src/test/java` or `src/test/resources`.", "Do 
not edit production, generated output, unrelated tests, or another file type.", 
"Capture `Task scope baseline` after resolving the file set and before 
editing.", "Any candidate-relevant changed path outside that set is a failure, 
including further mutation of a path already dirty at task start.", "Exclude 
only untracked or ignored reproducible ver [...]
+
+[[cases]]
+id = "same_review_basis_has_same_assessment_across_candidate_types"
+group = "review-pr assessment basis"
+description = "Keeps equal local and public content under the same applicable 
task requirement and its proven exception on one assessment."
+phase = "read-only review-basis assessment"
+ordinary_loop = false
+profile = "review-pr"
+semantic_policy_assertions = true
+prompt = "Answer this self-contained read-only review-policy question using 
only the supplied facts and bound rules; do not inspect files, run checks, edit 
anything or produce a Formal Review report. A and B review the same effective 
patch from the same original baseline, with equal content, delta, authoritative 
files, accepted requirements, Code Correctness focus and admissible evidence. A 
is a local candidate with pending edits; B is the committed public candidate, 
so their Git HEAD la [...]
+decision = "proceed"
+required_actions = []
+allowed_actions = []
+forbidden_actions = ["edit_code", "edit_non_code", "run_local_checks", 
"measure_performance", "run_pre_handoff_review", "propose_commit_message", 
"mutate_git", "mutate_remote"]
+required_reasons = ["read_only_request"]
+required_summary_terms = ["A=change-request", "B=change-request", 
"C=mergeable", "D=mergeable"]
+forbidden_summary_terms = ["A=continue-review", "A=mergeable", 
"A=review-incomplete", "A=needs-discussion", "B=continue-review", 
"B=mergeable", "B=review-incomplete", "B=needs-discussion", 
"C=continue-review", "C=review-incomplete", "C=change-request", 
"C=needs-discussion", "D=continue-review", "D=review-incomplete", 
"D=change-request", "D=needs-discussion"]
+critical = true
+policy_binding_profile = "review-pr"
+policy_assertions = [{source = "review-pr", statements = ["Record each 
applicable requirement's source, content version, scope and valid exceptions in 
readable basis source material; a task-only requirement applies only to its 
authorized task candidate and must not become a universal repository or 
public-PR rule.", "Keep effective content and delta identity separate from Git 
revision lineage so equal local and public candidates retain one 
code-correctness basis.", "Two reviews with the s [...]
+
+[[cases]]
+id = "unchanged_candidate_corrects_proven_prior_review_miss"
+group = "review-pr assessment corrections"
+description = "Corrects a proven earlier miss on unchanged inputs after a 
complete independent current assessment."
+phase = "read-only review-correction assessment"
+ordinary_loop = false
+profile = "review-pr"
+semantic_policy_assertions = true
+prompt = "Answer this self-contained read-only review-correction question 
using only the supplied facts and bound rules; do not inspect files, run 
checks, edit anything or produce a Formal Review report. An earlier 
complete-looking Formal Review returned Mergeable. The current effective 
candidate content and delta, original baseline, applicable requirements and 
exceptions, Code Correctness focus and admissible evidence are identical. No 
commit, repair, new requirement or external fact in [...]
+decision = "proceed"
+required_actions = []
+allowed_actions = []
+forbidden_actions = ["edit_code", "edit_non_code", "run_local_checks", 
"measure_performance", "run_pre_handoff_review", "propose_commit_message", 
"mutate_git", "mutate_remote"]
+required_reasons = ["read_only_request"]
+required_summary_terms = ["origin=previous-review-miss", 
"current=change-request", "prior-verdict=not-evidence"]
+forbidden_summary_terms = ["origin=introduced-by-latest-commits", 
"origin=exposed-by-previous-fix", "origin=changed-review-basis", 
"current=continue-review", "current=mergeable", "current=review-incomplete", 
"current=needs-discussion", "prior-verdict=evidence"]
+critical = true
+policy_binding_profile = "review-pr"
+policy_assertions = [{source = "review-pr", statements = ["For a proven 
previous-review miss, contradictory assessments on an unchanged basis, a 
triggered high-risk review criterion, or an explicit user request for 
independent review, obtain an independent read-only assessment through this 
workflow and the Completion Gate using the current basis and complete effective 
candidate without the previous verdict or an expected finding count.", "Never 
use a previous Formal Review result as\nevi [...]
diff --git a/.codex/skills/review-pr/SKILL.md b/.codex/skills/review-pr/SKILL.md
index 082448f7ed6..37ec75e104e 100644
--- a/.codex/skills/review-pr/SKILL.md
+++ b/.codex/skills/review-pr/SKILL.md
@@ -44,6 +44,9 @@ Resolve one review basis before discovery: the effective 
candidate, applicable r
 confirmed findings consolidated by fix boundary, needs-discussion conditions,
 incomplete-evidence gaps, and Completion Gate state.
 
+Record each applicable requirement's source, content version, scope and valid 
exceptions in readable basis source material; a task-only requirement applies 
only to its authorized task candidate and must not become a universal 
repository or public-PR rule.
+Keep effective content and delta identity separate from Git revision lineage 
so equal local and public candidates retain one code-correctness basis.
+
 Output mode must not affect candidate discovery, proof, classification,
 coverage, or convergence. Never use a previous Formal Review result as
 evidence or as a conclusion to match. Treat previous public findings only as
@@ -174,6 +177,8 @@ Apply this workflow to the canonical review basis without 
letting output mode or
 Start every Formal Review round with fresh coverage of the complete effective 
candidate and its affected behavior and contracts, preserving the original task 
baseline and attributed scope.
 Reuse only facts and verification results whose current-candidate validity is 
established; do not inherit reviewed-file statuses, discovery passes, proof 
conclusions, or convergence results.
 
+For a proven previous-review miss, contradictory assessments on an unchanged 
basis, a triggered high-risk review criterion, or an explicit user request for 
independent review, obtain an independent read-only assessment through this 
workflow and the Completion Gate using the current basis and complete effective 
candidate without the previous verdict or an expected finding count.
+
 1. Complete `Repository Code Policy References`, then establish the 
authoritative effective-candidate scope and applicable requirements.
 2. Complete the Problem and Project Commitment Gate and establish the accepted 
behavior boundary.
 3. Confirm the selected review focus and admissible evidence.
@@ -202,10 +207,33 @@ Apply the Completion Gate to every Formal Review and to a 
discussion reply that
 - Use `scripts/build_review_inventory.py --format json` when local refs are 
available; its Markdown is only a bounded summary.
 - Use `scripts/review_ledger.py` for multi-file, high-risk, or omission-prone 
review; it accounts for review state but does not judge correctness.
 - Initialize a new ledger for each round that requires one, from the full 
current inventory with pending file statuses and no inherited review passes.
+- Initialize with `--review-basis <JSON>` containing the selected focus and 
readable sources identified by role and origin, so the ledger binds their 
actual content versions to the effective candidate, original baseline and 
authoritative files.
+- Record each substantive changed block's behavior, applicable risk axes and 
requirement origins, strongest counter-evidence and current source anchors with 
`mark-file --coverage <JSON>`, and attach finding anchors with `add-finding 
--anchors <JSON>`.
+- Rebuild ledgers with an older schema or stale inputs without inheriting 
their completion state.
+- Validate the current basis and coverage before selecting a result; a passing 
ledger proves mechanical coverage and evidence freshness, while the reviewer 
must still establish semantic proof under the existing gates.
 - For a standalone local candidate with index or working-tree changes, provide 
the same exact task-path file to both scripts with `--candidate-files <path>`. 
The scripts resolve those paths from the computed merge-base through the 
working tree, include matching untracked files, and fail when a listed path is 
unchanged.
 - Mutate one ledger sequentially; do not run ledger commands concurrently.
 - Keep ledger data private and remove only the exact current-review ledger.
 
+Use these JSON shapes in local files passed to the arguments, replacing the 
example values with current evidence and block IDs from the initialized ledger:
+Sources use `rule`, `requirement` or `evidence` roles; `--anchors` takes an 
anchor-array file shaped like `anchors` below.
+
+```json
+{"focus": "Code Correctness Review", "sources": [{"role": "rule", "origin": 
"CODE_OF_CONDUCT.md", "path": "CODE_OF_CONDUCT.md"}]}
+```
+
+```json
+[
+  {
+    "block": "<block ID>", "behavior": "<verified behavior>", "risk_axes": 
["contracts"],
+    "requirements": ["CODE_OF_CONDUCT.md"], "counter_evidence": "<contrary 
scenario checked and result>",
+    "anchors": [{"side": "candidate", "path": "<changed repository-relative 
path>", "start_line": 1, "end_line": 2}]
+  }
+]
+```
+
+Use `base` for original content and `candidate` for effective content; text 
blocks need an anchor overlapping their changed lines, whole-object binary, 
mode or rename changes omit the line bounds, and `source` anchors require the 
declared source's matching origin and path.
+
 If the gate cannot pass because a gap satisfies the Review Incomplete Proof 
Gate, return `Review Incomplete` in Formal Review and state the incomplete 
evidence without a formal verdict in a discussion reply.
 When confirmed blockers coexist with a proven gap that could hide more 
blockers, list them as confirmed partial facts but do not present them as the 
complete change-request set.
 
diff --git a/.codex/skills/review-pr/references/review-corrections.md 
b/.codex/skills/review-pr/references/review-corrections.md
index 43f21f4c58d..30fc1dced44 100644
--- a/.codex/skills/review-pr/references/review-corrections.md
+++ b/.codex/skills/review-pr/references/review-corrections.md
@@ -25,6 +25,9 @@ Use public previous-round feedback only for public PR 
conclusions.
 For standalone local corrections, inherit the main Skill's task baseline, 
attributed local delta, authorized evidence boundary and selected mode's output 
contract.
 Do not expose private review notes, local chat iterations, raw inventory, or 
internal scratch state.
 
+Before changing an overall conclusion, compare the prior and current effective 
candidates, requirement sources and versions, applicable exceptions, review 
focus, evidence identities and finding sets; candidate-type or Git revision 
labels and a different previous verdict do not establish a changed basis.
+Correct a proven earlier error rather than preserving an unsupported previous 
verdict for consistency.
+
 For every prior public finding, classify the latest head as:
 
 - Fixed.
@@ -51,8 +54,9 @@ Classify every finding first reported after an earlier formal 
review:
   reachable or materially changed the evidence.
 - `Missed in the previous review`: the problem was already present in the
   previously reviewed head.
+- `Established by a changed review basis`: a proven applicable requirement, 
selected focus or decisive fact changed and newly supports a finding that the 
prior basis did not support.
 
-Prove the classification from the previous and current effective candidates.
+Prove the classification from the previous and current effective candidates 
and their applicable requirements, focus and decisive facts.
 Do not describe an earlier-candidate problem as newly introduced.
 For a previous-review miss, acknowledge it directly, rerun the full Completion 
Gate, and return the complete remaining blocker set rather than publishing only 
the late finding.
 
diff --git a/.codex/skills/review-pr/scripts/review_ledger.py 
b/.codex/skills/review-pr/scripts/review_ledger.py
index 6361cfa49be..913c70ba1cc 100755
--- a/.codex/skills/review-pr/scripts/review_ledger.py
+++ b/.codex/skills/review-pr/scripts/review_ledger.py
@@ -21,24 +21,28 @@
 from __future__ import annotations
 
 import argparse
+import difflib
+import hashlib
 import json
 import os
 import re
 import shutil
+import stat
+import subprocess
 import sys
 import tempfile
 import time
 from collections import Counter
-from pathlib import Path
+from pathlib import Path, PurePosixPath
 from typing import Any, Iterable
 
 from review_common import (
-    categorize, compare_github_files, final_paths, get_repo_root, 
parse_name_status, resolve_candidate_changes, run_git,
+    categorize, compare_github_files, get_repo_root, parse_name_status, 
resolve_candidate_changes, run_git,
 )
 
 
 LEDGER_KIND = "review-pr-coverage-ledger"
-LEDGER_VERSION = 3
+LEDGER_VERSION = 4
 LEDGER_FILE_NAME = "ledger.json"
 FILE_STATUSES = frozenset({"pending", "reviewed", "churn-only", 
"test-only-reviewed", "not-applicable", "blocked"})
 FINAL_FILE_STATUSES = FILE_STATUSES - {"pending"}
@@ -46,6 +50,8 @@ SUBSTANTIVE_FILE_STATUSES = frozenset({"reviewed", 
"test-only-reviewed"})
 EXPLAINED_FILE_STATUSES = frozenset({"churn-only", "not-applicable"})
 FINDING_STATUSES = frozenset({"candidate", "confirmed", "withdrawn", 
"review-incomplete-gap", "non-blocking", "out-of-scope"})
 PASS_FOCUSES = ("root-cause", "blast-radius", "tests-runtime", "convergence")
+REVIEW_FOCUSES = ("Code Correctness Review", "Mergeability Review", "CI 
Review")
+SOURCE_ROLES = frozenset({"rule", "requirement", "evidence"})
 
 
 def ledger_root() -> Path:
@@ -93,21 +99,38 @@ def validate_schema(ledger: object) -> None:
         "findings": list,
         "passes": list,
         "review_revision": int,
+        "basis": dict,
     }
     if any(not isinstance(ledger.get(key), expected_type) for key, 
expected_type in required_types.items()):
         raise RuntimeError("Malformed review coverage ledger")
     if not isinstance(ledger["scope"].get("github_files", {}), dict):
         raise RuntimeError("Malformed review coverage ledger scope")
+    scope_types = {"repo_root": str, "base_sha": str, "head_sha": str, 
"merge_base": str, "head_ref": str, "changed_file_count": int,
+                   "candidate_files": dict, "review_basis_file": str}
+    if any(not isinstance(ledger["scope"].get(key), value) for key, value in 
scope_types.items()):
+        raise RuntimeError("Malformed review coverage ledger scope")
+    basis_types = {"focus": str, "baseline": dict, "delta": list, "sources": 
list}
+    if any(not isinstance(ledger["basis"].get(key), value) for key, value in 
basis_types.items()):
+        raise RuntimeError("Malformed review coverage ledger basis")
+    candidate_files = ledger["scope"]["candidate_files"]
+    if not isinstance(candidate_files.get("provided"), bool) or not 
isinstance(candidate_files.get("path"), str):
+        raise RuntimeError("Malformed review coverage ledger candidate scope")
+    if any(not has_text(ledger["basis"]["baseline"].get(key)) for key in 
("commit", "tree")):
+        raise RuntimeError("Malformed review coverage ledger original 
baseline")
+    for entry in ledger["basis"]["delta"]:
+        if not isinstance(entry, dict) or not has_text(entry.get("path")) or 
any(not isinstance(entry.get(key), dict) for key in ("base", "candidate")):
+            raise RuntimeError("Malformed review coverage ledger delta")
     entry_schemas = (
         ("file", ledger["files"], {
             "path": str, "status": str, "clusters": list, "risk_axes": list, 
"findings": list,
+            "base": dict, "candidate": dict, "blocks": list, "coverage": list,
         }),
         ("finding", ledger["findings"], {
             "id": str, "status": str, "origin": str, "fix_boundary": str, 
"evidence": list, "full_path": list,
-            "counter_evidence": list, "necessity": str, "scope_proof": str, 
"files": list, "reason": str,
+            "counter_evidence": list, "necessity": str, "scope_proof": str, 
"files": list, "reason": str, "anchors": list,
         }),
         ("pass", ledger["passes"], {
-            "focus": str, "new_findings": int, "review_revision": int,
+            "focus": str, "new_findings": int, "review_revision": int, 
"basis_fingerprint": str,
         }),
     )
     for entry_name, entries, schema in entry_schemas:
@@ -120,6 +143,23 @@ def validate_schema(ledger: object) -> None:
         for entry in entries:
             if any(not all(isinstance(value, str) for value in entry[field]) 
for field in fields):
                 raise RuntimeError("Malformed review coverage ledger list 
entry")
+    for entry in ledger["files"]:
+        if "old_path" not in entry or entry["old_path"] is not None and not 
isinstance(entry["old_path"], str):
+            raise RuntimeError("Malformed review coverage ledger file entry")
+        for block in entry["blocks"]:
+            fields = {"id": str, "kind": str, "base_path": str, 
"candidate_path": str,
+                      "base_start": int, "base_count": int, "candidate_start": 
int, "candidate_count": int}
+            if not isinstance(block, dict) or any(not 
isinstance(block.get(key), value) for key, value in fields.items()):
+                raise RuntimeError("Malformed review coverage ledger block")
+        for coverage in entry["coverage"]:
+            validate_coverage_shape(coverage, captured=True)
+    for finding in ledger["findings"]:
+        for anchor in finding["anchors"]:
+            validate_anchor_shape(anchor, captured=True)
+    for source in ledger["basis"]["sources"]:
+        validate_source_shape(source)
+        if any(not has_text(source.get(key)) for key in ("mode", "sha256")):
+            raise RuntimeError("Malformed review coverage ledger source 
snapshot")
 
 
 def read_ledger(value: str | Path) -> dict[str, Any]:
@@ -162,6 +202,238 @@ def has_text_entries(values: Iterable[object]) -> bool:
     return bool(entries) and all(has_text(each) for each in entries)
 
 
+def digest(value: Any) -> str:
+    return hashlib.sha256(json.dumps(value, sort_keys=True, separators=(",", 
":")).encode()).hexdigest()
+
+
+def validate_source_shape(source: Any) -> None:
+    if not isinstance(source, dict) or any(not has_text(source.get(key)) for 
key in ("role", "origin", "path")) or source["role"] not in SOURCE_ROLES:
+        raise RuntimeError("Review basis sources require role, origin, and a 
readable material path")
+
+
+def validate_anchor_shape(anchor: Any, captured: bool = False) -> None:
+    if not isinstance(anchor, dict) or anchor.get("side") not in {"base", 
"candidate", "source"} or not has_text(anchor.get("path")):
+        raise RuntimeError("Source anchors require side and path")
+    if "source" == anchor["side"] and not has_text(anchor.get("origin")):
+        raise RuntimeError("Source anchors require an explicit declared 
origin")
+    if ("start_line" in anchor) != ("end_line" in anchor):
+        raise RuntimeError("Source anchors require both line bounds or 
neither")
+    for key in ("start_line", "end_line"):
+        if key in anchor and type(anchor[key]) is not int:
+            raise RuntimeError("Source anchor line bounds must be integers")
+    if captured and any(not has_text(anchor.get(key)) for key in ("sha256", 
"mode", "range_sha256")):
+        raise RuntimeError("Captured source anchors require content 
fingerprints")
+    if captured and any(key not in anchor for key in ("start_line", 
"end_line")):
+        raise RuntimeError("Captured source anchors require normalized line 
bounds")
+
+
+def validate_coverage_shape(coverage: Any, captured: bool = False) -> None:
+    if not isinstance(coverage, dict) or any(not has_text(coverage.get(key)) 
for key in ("block", "behavior", "counter_evidence")):
+        raise RuntimeError("Block coverage requires block, behavior, and 
counter-evidence")
+    if any(not isinstance(coverage.get(key), list) or not 
has_text_entries(coverage[key]) for key in ("risk_axes", "requirements")):
+        raise RuntimeError("Block coverage requires risk axes and applicable 
requirement origins")
+    if not isinstance(coverage.get("anchors"), list) or not 
coverage["anchors"]:
+        raise RuntimeError("Block coverage requires real source anchors")
+    for anchor in coverage["anchors"]:
+        validate_anchor_shape(anchor, captured=captured)
+
+
+def read_object(repo_root: Path, scope: dict[str, Any], side: str, path: str) 
-> tuple[dict[str, Any], bytes | None]:
+    if "source" != side and (PurePosixPath(path).is_absolute() or ".." in 
PurePosixPath(path).parts or "." == path):
+        raise RuntimeError(f"Invalid repository-relative source anchor path: 
{path}")
+    if "base" == side or "candidate" == side and not 
scope["candidate_files"]["provided"]:
+        ref = scope["merge_base"] if "base" == side else scope["head_ref"]
+        process = subprocess.run(["git", "ls-tree", "-z", ref, "--", path], 
cwd=repo_root, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
+                                 env={**os.environ, "GIT_LITERAL_PATHSPECS": 
"1"}, check=False)
+        if process.returncode:
+            raise RuntimeError(f"Cannot read {side} object: {path}")
+        objects = [each.split(b"\t", 1) for each in 
process.stdout.split(b"\0") if each]
+        entry = next((value for metadata, value in objects if value.decode() 
== path), None)
+        if entry is None:
+            return {"mode": None, "sha256": None}, None
+        metadata = next(metadata for metadata, value in objects if value == 
entry).decode().split()
+        mode, object_type, object_id = metadata
+        if "tree" == object_type:
+            return {"mode": None, "sha256": None}, None
+        if "commit" == object_type:
+            content = object_id.encode()
+        else:
+            blob = subprocess.run(["git", "cat-file", "blob", object_id], 
cwd=repo_root, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=False)
+            if blob.returncode:
+                raise RuntimeError(f"Cannot read {side} object bytes: {path}")
+            content = blob.stdout
+    else:
+        source_path = Path(path) if Path(path).is_absolute() else repo_root / 
path
+        try:
+            metadata = source_path.stat() if "source" == side else 
source_path.lstat()
+        except (FileNotFoundError, NotADirectoryError):
+            return {"mode": None, "sha256": None}, None
+        if stat.S_ISLNK(metadata.st_mode):
+            mode, content = "120000", os.fsencode(os.readlink(source_path))
+        elif stat.S_ISDIR(metadata.st_mode) and "candidate" == side:
+            index = subprocess.run(["git", "ls-files", "--stage", "-z", "--", 
path], cwd=repo_root, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
+                                   env={**os.environ, "GIT_LITERAL_PATHSPECS": 
"1"}, check=False)
+            if index.returncode:
+                raise RuntimeError(f"Cannot determine candidate Git type: 
{path}")
+            entries = [each.split(b"\t", 1) for each in 
index.stdout.split(b"\0") if each]
+            if not any(value.decode() == path and metadata.split()[0] == 
b"160000" for metadata, value in entries):
+                return {"mode": None, "sha256": None}, None
+            if get_repo_root(source_path).resolve() != source_path.resolve():
+                raise RuntimeError(f"Gitlink checkout must have its own 
repository: {path}")
+            if run_git(["status", "--porcelain", "--untracked-files=no"], 
source_path).strip():
+                raise RuntimeError(f"Gitlink checkout has staged or unstaged 
tracked changes: {path}")
+            mode, content = "160000", run_git(["rev-parse", "HEAD"], 
source_path).strip().encode()
+        else:
+            mode = "100755" if metadata.st_mode & stat.S_IXUSR else "100644"
+            content = source_path.read_bytes()
+    return {"mode": mode, "sha256": hashlib.sha256(content).hexdigest()}, 
content
+
+
+def changed_blocks(path: str, base_path: str, base: dict[str, Any], candidate: 
dict[str, Any], old: bytes | None, new: bytes | None) -> list[dict[str, Any]]:
+    result = []
+    try:
+        old_lines, new_lines = (old or 
b"").decode("utf-8").splitlines(keepends=True), (new or 
b"").decode("utf-8").splitlines(keepends=True)
+        opaque = b"\0" in (old or b"") or b"\0" in (new or b"") or 
base["mode"] in {"120000", "160000"} or candidate["mode"] in {"120000", 
"160000"}
+    except UnicodeDecodeError:
+        old_lines, new_lines, opaque = [], [], True
+    if old != new and opaque:
+        result.append({"kind": "binary", "base_start": 0, "base_count": 0, 
"candidate_start": 0, "candidate_count": 0})
+    elif old != new:
+        for tag, old_start, old_end, new_start, new_end in 
difflib.SequenceMatcher(None, old_lines, new_lines).get_opcodes():
+            if "equal" != tag:
+                result.append({"kind": "text", "base_start": old_start + 1, 
"base_count": old_end - old_start,
+                               "candidate_start": new_start + 1, 
"candidate_count": new_end - new_start})
+    if base_path != path:
+        result.append({"kind": "rename", "base_start": 0, "base_count": 0, 
"candidate_start": 0, "candidate_count": 0})
+    if base["mode"] and candidate["mode"] and base["mode"] != 
candidate["mode"]:
+        result.append({"kind": "mode", "base_start": 0, "base_count": 0, 
"candidate_start": 0, "candidate_count": 0})
+    if not result and base != candidate:
+        result.append({"kind": "object", "base_start": 0, "base_count": 0, 
"candidate_start": 0, "candidate_count": 0})
+    for block in result:
+        block.update({"base_path": base_path, "candidate_path": path})
+        block["id"] = digest({**block, "base": base, "candidate": candidate})
+    return result
+
+
+def capture_basis(scope: dict[str, Any]) -> tuple[dict[str, Any], 
list[dict[str, Any]]]:
+    repo_root = Path(scope["repo_root"])
+    configuration = 
json.loads(Path(scope["review_basis_file"]).read_text(encoding="utf-8"))
+    if not isinstance(configuration, dict) or configuration.get("focus") not 
in REVIEW_FOCUSES or not isinstance(configuration.get("sources"), list):
+        raise RuntimeError("Review basis requires a supported focus and 
declared sources")
+    sources = []
+    for source in configuration["sources"]:
+        validate_source_shape(source)
+        snapshot, content = read_object(repo_root, scope, "source", 
source["path"])
+        if content is None:
+            raise RuntimeError(f"Review basis source is unavailable: 
{source['origin']}")
+        sources.append({**source, **snapshot})
+    if not any(each["role"] in {"rule", "requirement"} for each in sources) or 
len(sources) != len({each["origin"] for each in sources}):
+        raise RuntimeError("Review basis requires unambiguous applicable rule 
or requirement sources")
+    candidate_files = scope["candidate_files"]
+    changes = resolve_candidate_changes(repo_root, scope["merge_base"], 
candidate_files["path"]) if candidate_files["provided"] else parse_name_status(
+        run_git(["diff", "--name-status", 
f"{scope['merge_base']}..{scope['head_ref']}"], repo_root))
+    files = []
+    paths = sorted({path for change in changes for path in (change.old_path or 
change.path, change.path)})
+    objects = {(side, path): read_object(repo_root, scope, side, path) for 
path in paths for side in ("base", "candidate")}
+    for change in changes:
+        base_path = change.old_path or change.path
+        base, old = objects[("base", base_path)]
+        candidate, new = objects[("candidate", change.path)]
+        files.append({"path": change.path, "old_path": change.old_path, 
"git_status": change.status, "base": base, "candidate": candidate,
+                      "blocks": changed_blocks(change.path, base_path, base, 
candidate, old, new)})
+    delta = [{"path": path, "base": objects[("base", path)][0], "candidate": 
objects[("candidate", path)][0]} for path in paths]
+    return {"focus": configuration["focus"], "baseline": {"commit": 
scope["merge_base"], "tree": run_git(["rev-parse", 
f"{scope['merge_base']}^{{tree}}"], repo_root).strip()},
+            "sources": sources, "delta": delta}, files
+
+
+def capture_anchor(anchor: dict[str, Any], ledger: dict[str, Any]) -> 
dict[str, Any]:
+    if "source" == anchor["side"]:
+        source = next((each for each in ledger["basis"]["sources"] if 
each["origin"] == anchor["origin"] and each["path"] == anchor["path"]), None)
+        if source is None:
+            raise RuntimeError("Source anchor does not match a declared 
material and origin")
+    snapshot, content = read_object(Path(ledger["scope"]["repo_root"]), 
ledger["scope"], anchor["side"], anchor["path"])
+    if content is None:
+        raise RuntimeError(f"Source anchor is unavailable: 
{anchor['side']}:{anchor['path']}")
+    start, end = anchor.get("start_line", 0), anchor.get("end_line", 0)
+    if start or end:
+        try:
+            lines = content.decode("utf-8").splitlines(keepends=True)
+        except UnicodeDecodeError as ex:
+            raise RuntimeError("Line anchors require UTF-8 text; use a 
whole-object anchor for binary evidence") from ex
+        if start < 1 or end < start or end > len(lines):
+            raise RuntimeError("Source anchor line range is outside the actual 
source")
+        selected = "".join(lines[start - 1:end]).encode("utf-8")
+    else:
+        selected = content
+    return {**anchor, **snapshot, "start_line": start, "end_line": end, 
"range_sha256": hashlib.sha256(selected).hexdigest()}
+
+
+def anchor_covers_block(anchor: dict[str, Any], block: dict[str, Any]) -> bool:
+    side = anchor["side"]
+    if side not in {"base", "candidate"} or anchor["path"] != 
block[f"{side}_path"]:
+        return False
+    if "text" != block["kind"]:
+        return not anchor["start_line"] and not anchor["end_line"]
+    count, start = block[f"{side}_count"], block[f"{side}_start"]
+    return count > 0 and anchor["start_line"] > 0 and anchor["start_line"] <= 
start + count - 1 and anchor["end_line"] >= start
+
+
+def coverage_errors(entry: dict[str, Any], requirement_origins: set[str]) -> 
list[str]:
+    errors = []
+    block_ids = {each["id"] for each in entry["blocks"]}
+    covered_blocks = {each["block"] for each in entry["coverage"]}
+    if covered_blocks - block_ids:
+        errors.append(f"{entry['path']} has unknown changed-block coverage")
+    if entry["status"] in SUBSTANTIVE_FILE_STATUSES and block_ids - 
covered_blocks:
+        errors.append(f"{entry['path']} has uncovered changed blocks: 
{len(block_ids - covered_blocks)}")
+    for coverage in entry["coverage"]:
+        if set(coverage["requirements"]) - requirement_origins:
+            errors.append(f"{entry['path']} block coverage references unknown 
applicable requirement origins")
+        block = next((each for each in entry["blocks"] if each["id"] == 
coverage["block"]), None)
+        if block is not None and not any(anchor_covers_block(each, block) for 
each in coverage["anchors"]):
+            errors.append(f"{entry['path']} block coverage has no anchor at 
the actual change")
+    return errors
+
+
+def all_anchors(ledger: dict[str, Any]) -> Iterable[dict[str, Any]]:
+    for entry in ledger["files"]:
+        for coverage in entry["coverage"]:
+            yield from coverage["anchors"]
+    for finding in ledger["findings"]:
+        yield from finding["anchors"]
+
+
+def basis_fingerprint(ledger: dict[str, Any]) -> str:
+    basis = ledger["basis"]
+    scope_paths = {each["path"] for each in basis["delta"]}
+    evidence = {(each["side"], each["path"], each["mode"], each["sha256"]) for 
each in all_anchors(ledger)
+                if "source" != each["side"] and each["path"] not in 
scope_paths}
+    return digest({"focus": basis["focus"], "baseline": 
basis["baseline"]["tree"], "delta": basis["delta"],
+                   "sources": sorted((each["role"], each["origin"], 
each["mode"], each["sha256"]) for each in basis["sources"]),
+                   "evidence": sorted(evidence)})
+
+
+def validate_current_basis(ledger: dict[str, Any]) -> list[str]:
+    current, files = capture_basis(ledger["scope"])
+    errors = []
+    if current != ledger["basis"]:
+        errors.append("Effective candidate, original baseline, focus, or 
declared review sources changed; initialize a new ledger")
+    snapshots = [{key: each[key] for key in ("path", "old_path", "base", 
"candidate", "blocks")} for each in ledger["files"]]
+    actual = [{key: each[key] for key in ("path", "old_path", "base", 
"candidate", "blocks")} for each in files]
+    if snapshots != actual or ledger["scope"]["changed_file_count"] != 
len(files):
+        errors.append("Ledger files, changed-file count, or changed blocks do 
not match the effective candidate")
+    if ledger["scope"].get("github_files_file"):
+        comparison = compare_github_files((each["path"] for each in files), 
ledger["scope"]["github_files_file"])
+        if comparison != ledger["scope"]["github_files"]:
+            errors.append("Authoritative GitHub file material changed or no 
longer matches the ledger")
+    if errors:
+        return errors
+    for anchor in all_anchors(ledger):
+        if capture_anchor(anchor, ledger) != anchor:
+            errors.append(f"Source anchor is stale: 
{anchor['side']}:{anchor['path']}")
+    return errors
+
+
 def sync_file_findings(ledger: dict[str, Any]) -> None:
     for each in ledger["files"]:
         each["findings"] = []
@@ -176,8 +448,21 @@ def cmd_init(args: argparse.Namespace) -> int:
     head_sha = run_git(["rev-parse", args.head_ref], repo_root).strip()
     merge_base = run_git(["merge-base", args.base_ref, args.head_ref], 
repo_root).strip()
     candidate_files = getattr(args, "candidate_files", None)
-    changed_files = resolve_candidate_changes(repo_root, merge_base, 
candidate_files) if candidate_files else parse_name_status(
-        run_git(["diff", "--name-status", f"{merge_base}..{args.head_ref}"], 
repo_root))
+    scope = {
+        "repo": repo_root.name,
+        "repo_root": str(repo_root),
+        "pr": args.pr,
+        "base_ref": args.base_ref,
+        "base_sha": base_sha,
+        "head_ref": args.head_ref,
+        "head_sha": head_sha,
+        "merge_base": merge_base,
+        "candidate_files": {"provided": bool(candidate_files), "path": 
str(Path(candidate_files).resolve()) if candidate_files else ""},
+        "review_basis_file": str(Path(args.review_basis).resolve()),
+        "github_files_file": str(Path(args.github_files).resolve()) if 
args.github_files else "",
+    }
+    basis, changed_files = capture_basis(scope)
+    scope.update({"changed_file_count": len(changed_files), "github_files": 
compare_github_files((each["path"] for each in changed_files), 
args.github_files)})
     ledger_dir = create_ledger_dir(repo_root, args.pr, head_sha)
     ledger_file = ledger_dir / LEDGER_FILE_NAME
     now = int(time.time())
@@ -187,27 +472,16 @@ def cmd_init(args: argparse.Namespace) -> int:
         "created_at": now,
         "updated_at": now,
         "review_revision": 0,
-        "scope": {
-            "repo": repo_root.name,
-            "pr": args.pr,
-            "base_ref": args.base_ref,
-            "base_sha": base_sha,
-            "head_ref": args.head_ref,
-            "head_sha": head_sha,
-            "merge_base": merge_base,
-            "changed_file_count": len(changed_files),
-            "candidate_files": {"provided": bool(candidate_files)},
-            "github_files": compare_github_files(final_paths(changed_files), 
args.github_files),
-        },
+        "scope": scope,
+        "basis": basis,
         "files": [{
-            "path": each.path,
-            "git_status": each.status,
-            "old_path": each.old_path,
-            "category": categorize(each.path),
+            **each,
+            "category": categorize(each["path"]),
             "status": "pending",
             "clusters": [],
             "risk_axes": [],
             "findings": [],
+            "coverage": [],
             "reason": "",
         } for each in changed_files],
         "findings": [],
@@ -222,11 +496,25 @@ def cmd_init(args: argparse.Namespace) -> int:
 def cmd_mark_file(args: argparse.Namespace) -> int:
     ledger_file = resolve_ledger_file(args.ledger)
     ledger = read_ledger(ledger_file)
+    errors = validate_current_basis(ledger)
+    if errors:
+        raise RuntimeError("; ".join(errors))
     entry = find_file_entry(ledger, args.path)
+    if args.coverage:
+        coverage = json.loads(Path(args.coverage).read_text(encoding="utf-8"))
+        if not isinstance(coverage, list):
+            raise RuntimeError("File coverage material must be a list of 
reviewed blocks")
+        for block_review in coverage:
+            validate_coverage_shape(block_review)
+            block_review["anchors"] = [capture_anchor(each, ledger) for each 
in block_review["anchors"]]
+        entry["coverage"] = coverage
     entry["status"] = args.status
     unique_extend(entry["clusters"], args.cluster or [])
     unique_extend(entry["risk_axes"], args.risk_axis or [])
     entry["reason"] = args.reason or ""
+    errors = coverage_errors(entry, {each["origin"] for each in 
ledger["basis"]["sources"] if each["role"] in {"rule", "requirement"}})
+    if errors:
+        raise RuntimeError("; ".join(errors))
     ledger["review_revision"] += 1
     write_ledger(ledger_file, ledger)
     print(f"marked={args.path} status={args.status}")
@@ -236,6 +524,14 @@ def cmd_mark_file(args: argparse.Namespace) -> int:
 def cmd_add_finding(args: argparse.Namespace) -> int:
     ledger_file = resolve_ledger_file(args.ledger)
     ledger = read_ledger(ledger_file)
+    errors = validate_current_basis(ledger)
+    if errors:
+        raise RuntimeError("; ".join(errors))
+    anchors = json.loads(Path(args.anchors).read_text(encoding="utf-8")) if 
args.anchors else []
+    if not isinstance(anchors, list):
+        raise RuntimeError("Finding anchors material must be a list")
+    for anchor in anchors:
+        validate_anchor_shape(anchor)
     finding = {
         "id": args.id,
         "status": args.status,
@@ -248,6 +544,7 @@ def cmd_add_finding(args: argparse.Namespace) -> int:
         "scope_proof": args.scope_proof or "",
         "files": args.file or [],
         "reason": args.reason or "",
+        "anchors": [capture_anchor(each, ledger) for each in anchors],
     }
     existing = next((each for each in ledger["findings"] if args.id == 
each["id"]), None)
     if existing:
@@ -264,6 +561,9 @@ def cmd_add_finding(args: argparse.Namespace) -> int:
 def cmd_add_pass(args: argparse.Namespace) -> int:
     ledger_file = resolve_ledger_file(args.ledger)
     ledger = read_ledger(ledger_file)
+    errors = validate_current_basis(ledger)
+    if errors:
+        raise RuntimeError("; ".join(errors))
     if 0 > args.new_findings:
         raise RuntimeError("New finding count must not be negative")
     ledger["passes"].append({
@@ -272,6 +572,7 @@ def cmd_add_pass(args: argparse.Namespace) -> int:
         "review_revision": ledger["review_revision"],
         "finding_count": len(ledger["findings"]),
         "created_at": int(time.time()),
+        "basis_fingerprint": basis_fingerprint(ledger),
     })
     write_ledger(ledger_file, ledger)
     print(f"pass={args.focus} new_findings={args.new_findings}")
@@ -280,7 +581,14 @@ def cmd_add_pass(args: argparse.Namespace) -> int:
 
 def validate_ledger(ledger: dict[str, Any], incomplete_result: bool = False) 
-> list[str]:
     validate_schema(ledger)
-    result: list[str] = []
+    return _validate_ledger(ledger, incomplete_result)
+
+
+def _validate_ledger(ledger: dict[str, Any], incomplete_result: bool = False) 
-> list[str]:
+    result = validate_current_basis(ledger)
+    requirement_origins = {each["origin"] for each in 
ledger["basis"]["sources"] if each["role"] in {"rule", "requirement"}}
+    for entry in ledger["files"]:
+        result.extend(coverage_errors(entry, requirement_origins))
     github_files = ledger["scope"].get("github_files", {})
     if github_files.get("provided") and not github_files.get("matched"):
         result.append("GitHub file list does not match local triple-dot scope")
@@ -347,6 +655,8 @@ def validate_ledger(ledger: dict[str, Any], 
incomplete_result: bool = False) ->
         if unknown_files:
             result.append(f"{each['id']} references files outside scope: {', 
'.join(unknown_files)}")
         if "confirmed" == each["status"]:
+            if not each["anchors"]:
+                result.append(f"{each['id']} confirmed finding is missing real 
source anchors")
             if not has_text(each["origin"]):
                 result.append(f"{each['id']} confirmed finding is missing 
origin")
             if not has_text(each["fix_boundary"]):
@@ -368,6 +678,8 @@ def validate_ledger(ledger: dict[str, Any], 
incomplete_result: bool = False) ->
                 missing_fact = "the unavailable fact" if incomplete_result 
else "a reason"
                 result.append(f"{each['id']} incomplete gap is missing 
{missing_fact}")
             if incomplete_result:
+                if not each["anchors"]:
+                    result.append(f"{each['id']} incomplete gap is missing 
real unavailability-proof anchors")
                 if not has_text(each["origin"]):
                     result.append(f"{each['id']} incomplete gap is missing the 
fact source")
                 if not has_text_entries(each["evidence"]):
@@ -384,7 +696,8 @@ def validate_ledger(ledger: dict[str, Any], 
incomplete_result: bool = False) ->
                     result.append(f"{each['id']} incomplete gap is missing 
affected scope files")
             else:
                 result.append(f"{each['id']} requires a Review Incomplete 
result")
-    pass_foci = {each.get("focus") for each in ledger["passes"]}
+    current_fingerprint = basis_fingerprint(ledger)
+    pass_foci = {each.get("focus") for each in ledger["passes"] if 
each["basis_fingerprint"] == current_fingerprint}
     missing_passes = [each for each in PASS_FOCUSES if each not in pass_foci]
     if missing_passes:
         result.append(f"Required review passes missing: {', 
'.join(missing_passes)}")
@@ -396,28 +709,30 @@ def validate_ledger(ledger: dict[str, Any], 
incomplete_result: bool = False) ->
             result.append("Latest convergence pass did not finish with zero 
new findings")
         if ledger["review_revision"] != final_pass.get("review_revision"):
             result.append("Latest convergence pass predates a file or finding 
change")
+        if current_fingerprint != final_pass["basis_fingerprint"]:
+            result.append("Latest convergence pass uses a different review 
basis")
     return result
 
 
 def cmd_validate(args: argparse.Namespace) -> int:
     ledger = read_ledger(args.ledger)
-    errors = validate_ledger(ledger)
+    errors = _validate_ledger(ledger)
     if errors:
         for each in errors:
             print(f"ERROR: {each}")
         return 1
-    print("OK")
+    print("OK: mechanical coverage and evidence are current; correctness 
requires Formal Review")
     return 0
 
 
 def cmd_validate_incomplete(args: argparse.Namespace) -> int:
     ledger = read_ledger(args.ledger)
-    errors = validate_ledger(ledger, incomplete_result=True)
+    errors = _validate_ledger(ledger, incomplete_result=True)
     if errors:
         for each in errors:
             print(f"ERROR: {each}")
         return 1
-    print("OK")
+    print("OK: mechanical incomplete-evidence record is current; 
classification requires Formal Review")
     return 0
 
 
@@ -430,6 +745,9 @@ def cmd_status(args: argparse.Namespace) -> int:
     print(f"findings={dict(sorted(finding_counts.items()))}")
     print(f"passes={dict(sorted(pass_counts.items()))}")
     print(f"review_revision={ledger['review_revision']}")
+    print(f"basis_fingerprint={basis_fingerprint(ledger)}")
+    for entry in ledger["files"]:
+        print(f"blocks={entry['path']} " + ",".join(each["id"] for each in 
entry["blocks"]))
     return 0
 
 
@@ -453,6 +771,7 @@ def build_parser() -> argparse.ArgumentParser:
     init.add_argument("--head-ref", required=True, help="PR head ref")
     init.add_argument("--github-files", help="File containing GitHub changed 
paths")
     init.add_argument("--candidate-files", help="Exact repository-relative 
local candidate paths, one per line")
+    init.add_argument("--review-basis", required=True, help="JSON focus and 
rule/requirement/evidence sources with role, origin, and material path")
     init.set_defaults(func=cmd_init)
     mark_file = subparsers.add_parser("mark-file", help="Set one authoritative 
file's coverage state")
     mark_file.add_argument("--ledger", required=True, help="Ledger file or 
directory")
@@ -461,6 +780,7 @@ def build_parser() -> argparse.ArgumentParser:
     mark_file.add_argument("--cluster", action="append", help="Behavior 
cluster containing this file")
     mark_file.add_argument("--risk-axis", action="append", help="Risk axis 
covered for this file")
     mark_file.add_argument("--reason", help="Reason for a non-substantive file 
classification")
+    mark_file.add_argument("--coverage", help="JSON changed-block coverage 
with behavior, risk axes, requirements, counter-evidence, and source anchors")
     mark_file.set_defaults(func=cmd_mark_file)
     finding = subparsers.add_parser("add-finding", help="Add or update one 
finding classification")
     finding.add_argument("--ledger", required=True, help="Ledger file or 
directory")
@@ -475,6 +795,7 @@ def build_parser() -> argparse.ArgumentParser:
     finding.add_argument("--scope-proof", help="Why the PR owns the finding")
     finding.add_argument("--file", action="append", help="Repository-relative 
scope file")
     finding.add_argument("--reason", help="Reason for an incomplete or 
non-blocking classification")
+    finding.add_argument("--anchors", help="JSON real source anchors 
supporting this finding or unavailable-evidence proof")
     finding.set_defaults(func=cmd_add_finding)
     review_pass = subparsers.add_parser("add-pass", help="Record a required 
review pass")
     review_pass.add_argument("--ledger", required=True, help="Ledger file or 
directory")
diff --git a/.codex/skills/review-pr/tests/test_review_ledger.py 
b/.codex/skills/review-pr/tests/test_review_ledger.py
index 8e22d1b7d96..986431d3434 100644
--- a/.codex/skills/review-pr/tests/test_review_ledger.py
+++ b/.codex/skills/review-pr/tests/test_review_ledger.py
@@ -15,11 +15,14 @@
 # limitations under the License.
 #
 
+import json
+import os
+import subprocess
 import sys
 import tempfile
 import unittest
 from argparse import Namespace
-from contextlib import redirect_stdout
+from contextlib import redirect_stderr, redirect_stdout
 from io import StringIO
 from pathlib import Path
 from unittest.mock import patch
@@ -29,12 +32,50 @@ SCRIPTS_DIR = Path(__file__).resolve().parents[1] / 
"scripts"
 if str(SCRIPTS_DIR) not in sys.path:
     sys.path.insert(0, str(SCRIPTS_DIR))
 
-import review_common as common
 import review_ledger as ledger
 
 
 class ReviewLedgerTest(unittest.TestCase):
 
+    def setUp(self):
+        temporary = tempfile.TemporaryDirectory()
+        self.addCleanup(temporary.cleanup)
+        self.private_root = Path(temporary.name)
+        self.repo = self.private_root / "repo"
+        self.repo.mkdir()
+        temporary_root = patch.object(ledger.tempfile, "gettempdir", 
return_value=str(self.private_root))
+        temporary_root.start()
+        self.addCleanup(temporary_root.stop)
+        self.source_path = "src/main/java/Foo.java"
+        self.owner_path = "src/main/java/Owner.java"
+        (self.repo / self.source_path).parent.mkdir(parents=True)
+        (self.repo / 
self.source_path).write_text("first\nold_first\nthird\nfourth\nfifth\nold_second\nlast\n",
 encoding="utf-8")
+        (self.repo / self.owner_path).write_text("Supported owner 
contract.\n", encoding="utf-8")
+        (self.repo / "Rules.md").write_text("Use the owning public 
boundary.\n", encoding="utf-8")
+        (self.repo / "Requirements.md").write_text("Review this candidate 
under the task scope and accepted exceptions.\n", encoding="utf-8")
+        self.git("init", "-q")
+        self.git("add", ".")
+        self.git("commit", "-qm", "Synthetic baseline")
+        self.base_sha = self.git("rev-parse", "HEAD").strip()
+        (self.repo / 
self.source_path).write_text("first\nnew_first\nthird\nfourth\nfifth\nnew_second\nlast\n",
 encoding="utf-8")
+        self.candidate_files = self.private_root / "candidate-files.txt"
+        self.candidate_files.write_text(self.source_path + "\n", 
encoding="utf-8")
+        self.attempt_log = self.private_root / "attempt.log"
+        self.attempt_log.write_text("Synthetic evidence route: unavailable 
target version; repository alternatives checked.\n", encoding="utf-8")
+        self.basis_file = self.private_root / "basis.json"
+        self.basis_file.write_text(json.dumps({"focus": "Code Correctness 
Review", "sources": [
+            {"role": "rule", "origin": "repository:Rules.md", "path": 
"Rules.md"},
+            {"role": "requirement", "origin": "user:synthetic-task", "path": 
"Requirements.md"},
+            {"role": "evidence", "origin": "attempt:target-version", "path": 
str(self.attempt_log)},
+        ]}), encoding="utf-8")
+
+    def git(self, *arguments):
+        environment = {**os.environ, "GIT_CONFIG_NOSYSTEM": "1", 
"GIT_CONFIG_GLOBAL": os.devnull,
+                       "GIT_AUTHOR_NAME": "Synthetic Review", 
"GIT_COMMITTER_NAME": "Synthetic Review",
+                       "GIT_AUTHOR_EMAIL": "[email protected]", 
"GIT_COMMITTER_EMAIL": "[email protected]"}
+        return subprocess.run(["git", "-c", "core.hooksPath=" + os.devnull, 
*arguments], cwd=self.repo, env=environment,
+                              check=True, stdout=subprocess.PIPE, 
stderr=subprocess.PIPE, text=True).stdout
+
     def test_complete_ledger_is_valid(self):
         self.assertEqual([], 
ledger.validate_ledger(self.create_valid_ledger()))
 
@@ -52,6 +93,7 @@ class ReviewLedgerTest(unittest.TestCase):
             "scope_proof": " ",
             "files": [""],
             "reason": "",
+            "anchors": [],
         }]
         errors = ledger.validate_ledger(actual_ledger)
         self.assertIn("F1 confirmed finding is missing origin", errors)
@@ -91,19 +133,11 @@ class ReviewLedgerTest(unittest.TestCase):
         self.assertIn("GitHub file list does not match local triple-dot 
scope", errors)
 
     def test_init_uses_exact_candidate_files(self):
-        changed_files = [common.ChangedFile(status="M", path="AGENTS.md")]
-        args = Namespace(repo_root=".", pr="local-task", base_ref="base", 
head_ref="head", github_files=None,
-                         candidate_files="candidate.txt")
-        with patch.object(ledger, "get_repo_root", 
return_value=Path("/repo")), \
-                patch.object(ledger, "run_git", side_effect=["base-sha\n", 
"head-sha\n", "merge-base\n"]), \
-                patch.object(ledger, "resolve_candidate_changes", 
return_value=changed_files) as resolve_candidate_changes, \
-                patch.object(ledger, "create_ledger_dir", 
return_value=Path("ledger")), \
-                patch.object(ledger, "write_ledger") as write_ledger, 
redirect_stdout(StringIO()):
-            self.assertEqual(0, ledger.cmd_init(args))
-        actual = write_ledger.call_args.args[1]
-        self.assertEqual({"provided": True}, 
actual["scope"]["candidate_files"])
-        self.assertEqual("AGENTS.md", actual["files"][0]["path"])
-        resolve_candidate_changes.assert_called_once_with(Path("/repo"), 
"merge-base", "candidate.txt")
+        actual = self.initialize_ledger()
+        self.assertEqual({"provided": True, "path": 
str(self.candidate_files.resolve())}, actual["scope"]["candidate_files"])
+        self.assertEqual([self.source_path], [each["path"] for each in 
actual["files"]])
+        self.assertEqual(2, len(actual["files"][0]["blocks"]))
+        self.assertEqual(self.base_sha, actual["basis"]["baseline"]["commit"])
 
     def test_confirmed_findings_must_have_distinct_fix_boundaries(self):
         actual_ledger = self.create_valid_ledger()
@@ -139,6 +173,7 @@ class ReviewLedgerTest(unittest.TestCase):
             "scope_proof": " ",
             "files": [""],
             "reason": " ",
+            "anchors": [],
         }]
         errors = ledger.validate_ledger(actual_ledger, incomplete_result=True)
         expected_errors = [
@@ -195,32 +230,343 @@ class ReviewLedgerTest(unittest.TestCase):
             with self.assertRaisesRegex(RuntimeError, "outside the private 
review ledger root"):
                 ledger.ensure_safe_ledger_file(outside_file)
 
-    @staticmethod
-    def create_valid_ledger():
-        review_passes = [{
-            "focus": focus,
-            "new_findings": 0,
-            "review_revision": 1,
-        } for focus in ledger.PASS_FOCUSES]
-        return {
-            "kind": ledger.LEDGER_KIND,
-            "version": ledger.LEDGER_VERSION,
-            "scope": {"github_files": {"provided": False}},
-            "files": [{
-                "path": "src/main/java/Foo.java",
-                "status": "reviewed",
-                "clusters": ["core"],
-                "risk_axes": ["correctness"],
-                "findings": [],
-                "reason": "",
-            }],
-            "findings": [],
-            "passes": review_passes,
-            "review_revision": 1,
-        }
+    def test_old_ledger_version_requires_new_review(self):
+        actual = self.create_valid_ledger()
+        actual["version"] = 3
+        with self.assertRaisesRegex(RuntimeError, "Unsupported or invalid"):
+            ledger.validate_ledger(actual)
+
+    def test_scope_count_must_match_actual_candidate(self):
+        actual = self.create_valid_ledger()
+        actual["scope"]["changed_file_count"] = 2
+        self.assertIn("Ledger files, changed-file count, or changed blocks do 
not match the effective candidate", ledger.validate_ledger(actual))
+
+    def test_persisted_nested_shapes_have_cli_diagnostics(self):
+        original = self.create_valid_ledger()
+        cases = [
+            ("scope", "candidate_files", {}, "candidate scope"),
+            ("basis", "baseline", {}, "original baseline"),
+            ("basis", "delta", [{}], "delta"),
+            ("scope", "base_sha", None, "scope"),
+            ("scope", "head_sha", None, "scope"),
+        ]
+        for section, key, value, diagnostic in cases:
+            with self.subTest(key=key):
+                actual = json.loads(json.dumps(original))
+                actual[section][key] = value
+                self.ledger_file.write_text(json.dumps(actual), 
encoding="utf-8")
+                output = StringIO()
+                with patch.object(sys, "argv", ["review_ledger.py", 
"validate", "--ledger", str(self.ledger_file)]), redirect_stderr(output):
+                    self.assertEqual(1, ledger.main())
+                self.assertEqual(f"error: Malformed review coverage ledger 
{diagnostic}\n", output.getvalue())
+
+    def test_every_changed_block_requires_coverage(self):
+        actual = self.create_valid_ledger()
+        actual["files"][0]["coverage"].pop()
+        self.assertIn(f"{self.source_path} has uncovered changed blocks: 1", 
ledger.validate_ledger(actual))
+
+    def test_explained_file_status_preserves_non_substantive_boundary(self):
+        original = self.create_valid_ledger()
+        for status in ("churn-only", "not-applicable"):
+            with self.subTest(status=status):
+                actual = json.loads(json.dumps(original))
+                actual["files"][0].update({"status": status, "reason": 
"Current evidence supports this non-substantive classification.", "coverage": 
[]})
+                for review_pass in actual["passes"]:
+                    review_pass["basis_fingerprint"] = 
ledger.basis_fingerprint(actual)
+                self.assertEqual([], ledger.validate_ledger(actual))
+
+    def test_missing_changed_block_cannot_hide_unreviewed_code(self):
+        actual = self.create_valid_ledger()
+        actual["files"][0]["blocks"].pop()
+        self.assertIn("Ledger files, changed-file count, or changed blocks do 
not match the effective candidate", ledger.validate_ledger(actual))
+
+    def test_unknown_changed_block_coverage_is_rejected(self):
+        actual = self.create_valid_ledger()
+        actual["files"][0]["coverage"][0]["block"] = "unknown-block"
+        self.assertIn(f"{self.source_path} has unknown changed-block 
coverage", ledger.validate_ledger(actual))
+
+    def test_reviewed_block_requires_known_requirement_origin(self):
+        actual = self.create_valid_ledger()
+        actual["files"][0]["coverage"][0]["requirements"] = ["unrelated-task"]
+        self.assertIn(f"{self.source_path} block coverage references unknown 
applicable requirement origins", ledger.validate_ledger(actual))
+
+    def test_block_anchor_must_cover_changed_lines(self):
+        actual = self.create_valid_ledger()
+        coverage = actual["files"][0]["coverage"]
+        coverage[0]["anchors"] = [{"side": "candidate", "path": 
self.source_path, "start_line": 3, "end_line": 3}]
+        coverage_file = self.private_root / "incorrect-coverage.json"
+        coverage_file.write_text(json.dumps(coverage), encoding="utf-8")
+        args = ledger.build_parser().parse_args(["mark-file", "--ledger", 
str(self.ledger_file), "--path", self.source_path, "--status", "reviewed",
+                                                 "--coverage", 
str(coverage_file)])
+        with self.assertRaisesRegex(RuntimeError, "no anchor at the actual 
change"):
+            ledger.cmd_mark_file(args)
+        self.assertEqual(actual["files"][0]["coverage"][1], 
ledger.read_ledger(self.ledger_file)["files"][0]["coverage"][1])
+
+    def test_source_anchor_rejects_wrong_origin_and_range(self):
+        self.create_valid_ledger()
+        invalid = [
+            {"side": "source", "path": str(self.attempt_log), "origin": 
"unknown-origin"},
+            {"side": "source", "path": "Requirements.md", "origin": 
"attempt:target-version"},
+            {"side": "candidate", "path": self.source_path, "start_line": 1, 
"end_line": 80},
+            {"side": "candidate", "path": self.source_path, "start_line": 
True, "end_line": 1},
+        ]
+        for anchor in invalid:
+            with self.subTest(anchor=anchor), self.assertRaises(RuntimeError):
+                anchors_file = self.private_root / "invalid-anchors.json"
+                anchors_file.write_text(json.dumps([anchor]), encoding="utf-8")
+                args = ledger.build_parser().parse_args(["add-finding", 
"--ledger", str(self.ledger_file), "--id", "F1", "--status", "non-blocking",
+                                                         "--anchors", 
str(anchors_file)])
+                ledger.cmd_add_finding(args)
+
+    def test_changed_candidate_and_scope_invalidate_review(self):
+        actual = self.create_valid_ledger()
+        (self.repo / self.source_path).write_text("A new effective 
candidate.\n", encoding="utf-8")
+        self.assertIn("Effective candidate, original baseline, focus, or 
declared review sources changed; initialize a new ledger", 
ledger.validate_ledger(actual))
+        (self.repo / "new.txt").write_text("Additional task change.\n", 
encoding="utf-8")
+        self.candidate_files.write_text(self.source_path + "\nnew.txt\n", 
encoding="utf-8")
+        self.assertIn("Ledger files, changed-file count, or changed blocks do 
not match the effective candidate", ledger.validate_ledger(actual))
+
+    def test_changed_rules_requirements_and_focus_invalidate_review(self):
+        actual = self.create_valid_ledger()
+        original_basis = self.basis_file.read_text(encoding="utf-8")
+        for changed in ("Rules.md", "Requirements.md", "focus"):
+            with self.subTest(changed=changed):
+                if "focus" == changed:
+                    configuration = json.loads(original_basis)
+                    configuration["focus"] = "CI Review"
+                    self.basis_file.write_text(json.dumps(configuration), 
encoding="utf-8")
+                else:
+                    path = self.repo / changed
+                    original = path.read_text(encoding="utf-8")
+                    path.write_text("Changed actual source commitment.\n", 
encoding="utf-8")
+                self.assertIn("Effective candidate, original baseline, focus, 
or declared review sources changed; initialize a new ledger", 
ledger.validate_ledger(actual))
+                if "focus" == changed:
+                    self.basis_file.write_text(original_basis, 
encoding="utf-8")
+                else:
+                    path.write_text(original, encoding="utf-8")
+
+    def test_source_and_range_fingerprints_are_recomputed(self):
+        actual = self.create_valid_ledger()
+        actual["files"][0]["coverage"][0]["anchors"][0]["range_sha256"] = "f" 
* 64
+        self.assertIn(f"Source anchor is stale: candidate:{self.source_path}", 
ledger.validate_ledger(actual))
+
+    def test_changed_unchanged_owner_evidence_invalidates_review(self):
+        actual = self.create_valid_ledger()
+        (self.repo / self.owner_path).write_text("Changed owner 
commitment.\n", encoding="utf-8")
+        self.assertIn(f"Source anchor is stale: candidate:{self.owner_path}", 
ledger.validate_ledger(actual))
+
+    def test_additional_source_evidence_invalidates_prior_passes(self):
+        actual = self.create_valid_ledger()
+        (self.repo / "new-owner.txt").write_text("Additional relevant owner 
evidence.\n", encoding="utf-8")
+        
actual["files"][0]["coverage"][0]["anchors"].append(ledger.capture_anchor({"side":
 "candidate", "path": "new-owner.txt"}, actual))
+        errors = ledger.validate_ledger(actual)
+        self.assertIn("Required review passes missing: root-cause, 
blast-radius, tests-runtime, convergence", errors)
+        self.assertIn("Latest convergence pass uses a different review basis", 
errors)
+
+    def test_metadata_only_change_preserves_discovery_pass_basis(self):
+        actual = self.create_valid_ledger()
+        actual["files"][0]["reason"] = "More precise wording with unchanged 
evidence."
+        actual["review_revision"] += 1
+        actual["passes"][-1]["review_revision"] = actual["review_revision"]
+        self.assertEqual([], ledger.validate_ledger(actual))
+
+    def test_add_pass_binds_basis_and_rejects_changed_candidate(self):
+        actual = self.create_valid_ledger()
+        args = Namespace(ledger=str(self.ledger_file), focus="convergence", 
new_findings=0)
+        with redirect_stdout(StringIO()):
+            self.assertEqual(0, ledger.cmd_add_pass(args))
+        self.assertEqual(actual["passes"][-1]["basis_fingerprint"], 
ledger.read_ledger(self.ledger_file)["passes"][-1]["basis_fingerprint"])
+        (self.repo / self.source_path).write_text("A changed candidate must 
not inherit passes.\n", encoding="utf-8")
+        with self.assertRaisesRegex(RuntimeError, "initialize a new ledger"):
+            ledger.cmd_add_pass(args)
+
+    def 
test_same_effective_contents_match_local_and_committed_review_basis(self):
+        local = self.create_valid_ledger()
+        self.git("add", self.source_path)
+        self.git("commit", "-qm", "Synthetic candidate")
+        public = self.create_valid_ledger(committed=True)
+        self.assertNotEqual(local["scope"]["head_sha"], 
public["scope"]["head_sha"])
+        self.assertEqual(ledger.basis_fingerprint(local), 
ledger.basis_fingerprint(public))
+        self.assertEqual([], ledger.validate_ledger(local))
+        self.assertEqual([], ledger.validate_ledger(public))
+
+    def test_rename_identity_does_not_depend_on_git_status_heuristics(self):
+        renamed_path = "src/main/java/Renamed.java"
+        (self.repo / self.source_path).rename(self.repo / renamed_path)
+        self.candidate_files.write_text(self.source_path + "\n" + renamed_path 
+ "\n", encoding="utf-8")
+        local = self.create_valid_ledger()
+        self.git("add", self.source_path, renamed_path)
+        self.git("commit", "-qm", "Synthetic rename")
+        public = self.create_valid_ledger(committed=True)
+        self.assertEqual(ledger.basis_fingerprint(local), 
ledger.basis_fingerprint(public))
+        self.assertEqual([], ledger.validate_ledger(public))
+
+    def 
test_deleted_added_empty_binary_and_mode_changes_have_real_coverage(self):
+        cases = ("deleted", "empty", "binary", "mode")
+        for scenario in cases:
+            with self.subTest(scenario=scenario):
+                path = self.repo / self.source_path
+                if not path.exists():
+                    
path.write_text("first\nold_first\nthird\nfourth\nfifth\nold_second\nlast\n", 
encoding="utf-8")
+                path.chmod(0o644)
+                if "deleted" == scenario:
+                    path.unlink()
+                elif "empty" == scenario:
+                    path = self.repo / "empty.bin"
+                    path.write_bytes(b"")
+                elif "binary" == scenario:
+                    path.write_bytes(b"\x00\xffchanged binary")
+                else:
+                    
path.write_text("first\nold_first\nthird\nfourth\nfifth\nold_second\nlast\n", 
encoding="utf-8")
+                    path.chmod(0o755)
+                
self.candidate_files.write_text(path.relative_to(self.repo).as_posix() + "\n", 
encoding="utf-8")
+                actual = self.create_valid_ledger()
+                self.assertEqual([], ledger.validate_ledger(actual))
+                expected_kind = {"deleted": "text", "empty": "object", 
"binary": "binary", "mode": "mode"}[scenario]
+                self.assertIn(expected_kind, {each["kind"] for each in 
actual["files"][0]["blocks"]})
+
+    def test_confirmed_prose_requires_real_source_anchors(self):
+        actual = self.create_valid_ledger()
+        finding = self.create_confirmed_finding("F1")
+        finding.update({"evidence": ["x"], "full_path": ["x"], 
"counter_evidence": ["x"], "necessity": "x", "scope_proof": "x", "anchors": []})
+        actual["findings"] = [finding]
+        self.assertIn("F1 confirmed finding is missing real source anchors", 
ledger.validate_ledger(actual))
+
+    def test_deleted_file_replaced_by_directory_is_not_a_gitlink(self):
+        path = self.repo / self.source_path
+        path.unlink()
+        path.mkdir()
+        actual = self.create_valid_ledger()
+        self.assertEqual({"mode": None, "sha256": None}, 
actual["files"][0]["candidate"])
+        self.assertEqual("D", actual["files"][0]["git_status"])
+        self.assertEqual([], ledger.validate_ledger(actual))
+        child = self.repo / "container/child.txt"
+        child.parent.mkdir()
+        child.write_text("Tracked child.\n", encoding="utf-8")
+        self.git("add", "container/child.txt")
+        self.git("commit", "-qm", "Synthetic child baseline")
+        self.base_sha = self.git("rev-parse", "HEAD").strip()
+        child.unlink()
+        child.parent.rmdir()
+        child.parent.write_text("A parent file replaces the directory.\n", 
encoding="utf-8")
+        self.candidate_files.write_text("container/child.txt\ncontainer\n", 
encoding="utf-8")
+        actual = self.create_valid_ledger()
+        self.assertEqual({"mode": None, "sha256": None}, 
actual["files"][0]["candidate"])
+        self.assertEqual("D", actual["files"][0]["git_status"])
+        self.assertEqual({"mode": None, "sha256": None}, 
actual["files"][1]["base"])
+        self.assertEqual("100644", actual["files"][1]["candidate"]["mode"])
+        self.assertEqual([], ledger.validate_ledger(actual))
+
+    def test_symlink_evidence_uses_link_payload(self):
+        path = self.repo / self.source_path
+        path.unlink()
+        path.symlink_to("../../missing-target")
+        actual = self.create_valid_ledger()
+        self.assertEqual("120000", actual["files"][0]["candidate"]["mode"])
+        self.assertEqual([], ledger.validate_ledger(actual))
+
+    def test_gitlink_head_change_invalidates_review_basis(self):
+        checkout = self.create_gitlink_candidate()
+        actual = self.create_valid_ledger()
+        (checkout / "contract.txt").write_text("Third gitlink commit.\n", 
encoding="utf-8")
+        self.git("-C", str(checkout), "add", "contract.txt")
+        self.git("-C", str(checkout), "commit", "-qm", "Synthetic gitlink C")
+        self.assertIn("Effective candidate, original baseline, focus, or 
declared review sources changed; initialize a new ledger", 
ledger.validate_ledger(actual))
+
+    def test_same_gitlink_pointer_matches_local_and_committed_basis(self):
+        checkout = self.create_gitlink_candidate()
+        local = self.create_valid_ledger()
+        pointer = self.git("-C", str(checkout), "rev-parse", "HEAD").strip()
+        self.git("update-index", "--cacheinfo", "160000," + pointer + 
",dependency")
+        self.git("commit", "-qm", "Synthetic gitlink candidate")
+        public = self.create_valid_ledger(committed=True)
+        self.assertEqual("160000", local["files"][0]["candidate"]["mode"])
+        self.assertEqual(local["files"][0]["candidate"], 
public["files"][0]["candidate"])
+        self.assertEqual(ledger.basis_fingerprint(local), 
ledger.basis_fingerprint(public))
+        self.assertEqual([], ledger.validate_ledger(local))
+        self.assertEqual([], ledger.validate_ledger(public))
+
+    def test_gitlink_cannot_read_parent_repository_head(self):
+        checkout = self.create_gitlink_candidate()
+        pointer = self.git("-C", str(checkout), "rev-parse", "HEAD").strip()
+        self.git("update-index", "--cacheinfo", "160000," + pointer + 
",dependency")
+        (checkout / ".git").rename(self.private_root / "detached-child-git")
+        with self.assertRaisesRegex(RuntimeError, "Gitlink checkout must have 
its own repository"):
+            self.initialize_ledger()
+
+    def test_dirty_gitlink_cannot_reuse_pointer_basis(self):
+        checkout = self.create_gitlink_candidate()
+        actual = self.create_valid_ledger()
+        (checkout / "contract.txt").write_text("Uncommitted tracked 
changes.\n", encoding="utf-8")
+        for staged in (False, True):
+            with self.subTest(staged=staged):
+                if staged:
+                    self.git("-C", str(checkout), "add", "contract.txt")
+                with self.assertRaisesRegex(RuntimeError, "Gitlink checkout 
has staged or unstaged tracked changes"):
+                    ledger.validate_ledger(actual)
+
+    def create_gitlink_candidate(self):
+        checkout = self.repo / "dependency"
+        checkout.mkdir()
+        self.git("-C", str(checkout), "init", "-q")
+        (checkout / "contract.txt").write_text("First gitlink commit.\n", 
encoding="utf-8")
+        self.git("-C", str(checkout), "add", "contract.txt")
+        self.git("-C", str(checkout), "commit", "-qm", "Synthetic gitlink A")
+        pointer = self.git("-C", str(checkout), "rev-parse", "HEAD").strip()
+        self.git("update-index", "--add", "--cacheinfo", "160000," + pointer + 
",dependency")
+        self.git("commit", "-qm", "Synthetic gitlink baseline")
+        self.base_sha = self.git("rev-parse", "HEAD").strip()
+        (checkout / "contract.txt").write_text("Second gitlink commit.\n", 
encoding="utf-8")
+        self.git("-C", str(checkout), "add", "contract.txt")
+        self.git("-C", str(checkout), "commit", "-qm", "Synthetic gitlink B")
+        self.candidate_files.write_text("dependency\n", encoding="utf-8")
+        return checkout
+
+    def test_incomplete_gap_requires_real_attempt_proof(self):
+        actual = self.create_valid_ledger()
+        finding = self.create_incomplete_gap()
+        finding["anchors"] = []
+        actual["findings"] = [finding]
+        self.assertIn("G1 incomplete gap is missing real unavailability-proof 
anchors", ledger.validate_ledger(actual, incomplete_result=True))
+
+    def initialize_ledger(self, committed=False):
+        arguments = ["init", "--repo-root", str(self.repo), "--pr", 
"synthetic-review", "--base-ref", self.base_sha,
+                     "--head-ref", "HEAD", "--review-basis", 
str(self.basis_file)]
+        if not committed:
+            arguments.extend(["--candidate-files", str(self.candidate_files)])
+        output = StringIO()
+        with redirect_stdout(output):
+            ledger.cmd_init(ledger.build_parser().parse_args(arguments))
+        self.ledger_file = 
Path(output.getvalue().splitlines()[0].removeprefix("ledger="))
+        return ledger.read_ledger(self.ledger_file)
+
+    def create_valid_ledger(self, committed=False):
+        actual = self.initialize_ledger(committed)
+        for entry in actual["files"]:
+            coverage = []
+            for block in entry["blocks"]:
+                side = "candidate" if entry["candidate"]["mode"] else "base"
+                if "text" == block["kind"] and not block[f"{side}_count"]:
+                    side = "base"
+                anchor = {"side": side, "path": block[f"{side}_path"]}
+                if "text" == block["kind"]:
+                    anchor.update({"start_line": block[f"{side}_start"], 
"end_line": block[f"{side}_start"] + block[f"{side}_count"] - 1})
+                coverage.append({"block": block["id"], "behavior": "Review the 
changed owning behavior.", "risk_axes": ["correctness"],
+                                 "requirements": ["repository:Rules.md", 
"user:synthetic-task"], "counter_evidence": "Check the stable owner contract.",
+                                 "anchors": [anchor, {"side": "candidate", 
"path": self.owner_path, "start_line": 1, "end_line": 1}]})
+            coverage_file = self.private_root / "coverage.json"
+            coverage_file.write_text(json.dumps(coverage), encoding="utf-8")
+            args = ledger.build_parser().parse_args(["mark-file", "--ledger", 
str(self.ledger_file), "--path", entry["path"], "--status", "reviewed",
+                                                     "--cluster", "core", 
"--risk-axis", "correctness", "--coverage", str(coverage_file)])
+            with redirect_stdout(StringIO()):
+                ledger.cmd_mark_file(args)
+        actual = ledger.read_ledger(self.ledger_file)
+        actual["passes"] = [{"focus": focus, "new_findings": 0, 
"review_revision": actual["review_revision"],
+                             "basis_fingerprint": 
ledger.basis_fingerprint(actual)} for focus in ledger.PASS_FOCUSES]
+        ledger.write_ledger(self.ledger_file, actual)
+        return actual
 
-    @staticmethod
-    def create_confirmed_finding(finding_id):
+    def create_confirmed_finding(self, finding_id):
         return {
             "id": finding_id,
             "status": "confirmed",
@@ -233,10 +579,10 @@ class ReviewLedgerTest(unittest.TestCase):
             "scope_proof": "introduced by reviewed change",
             "files": ["src/main/java/Foo.java"],
             "reason": "",
+            "anchors": 
ledger.read_ledger(self.ledger_file)["files"][0]["coverage"][0]["anchors"],
         }
 
-    @staticmethod
-    def create_incomplete_gap():
+    def create_incomplete_gap(self):
         return {
             "id": "G1",
             "status": "review-incomplete-gap",
@@ -249,6 +595,8 @@ class ReviewLedgerTest(unittest.TestCase):
             "scope_proof": "The reviewed file invokes the version-specific 
behavior.",
             "files": ["src/main/java/Foo.java"],
             "reason": "The target tool version is unavailable.",
+            "anchors": [ledger.capture_anchor({"side": "source", "path": 
str(self.attempt_log), "origin": "attempt:target-version", "start_line": 1, 
"end_line": 1},
+                                               
ledger.read_ledger(self.ledger_file))],
         }
 
 

Reply via email to