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 45790220bd4 Use a cumulative findings ledger for PR review convergence 
(#40307)
45790220bd4 is described below

commit 45790220bd444969d20d7dc97e2e3b49c56517fd
Author: Liang Zhang <[email protected]>
AuthorDate: Wed Oct 21 16:08:07 2026 +0800

    Use a cumulative findings ledger for PR review convergence (#40307)
    
    * Limit PR review confirmation to six independent assessments
    
    * Use a cumulative findings ledger for PR review convergence
    
    * Fix review ledger evidence freshness and historical stage validation
---
 .codex/harness/agents/cases.toml                   |  56 ++---
 .codex/skills/review-pr/SKILL.md                   |   7 +-
 .../review-pr/references/review-coordination.md    |  51 ++--
 .codex/skills/review-pr/scripts/review_ledger.py   | 140 ++++++-----
 .../skills/review-pr/tests/test_review_ledger.py   | 271 +++++++++++++++++----
 5 files changed, 355 insertions(+), 170 deletions(-)

diff --git a/.codex/harness/agents/cases.toml b/.codex/harness/agents/cases.toml
index 302248e3946..10178e95912 100644
--- a/.codex/harness/agents/cases.toml
+++ b/.codex/harness/agents/cases.toml
@@ -3835,7 +3835,7 @@ required_summary_terms = ["A=change-request", 
"B=change-request", "C=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 [...]
+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.", "Completed coordinated  [...]
 
 [[cases]]
 id = "unchanged_candidate_corrects_proven_prior_review_miss"
@@ -3845,7 +3845,7 @@ 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 [...]
+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 = []
@@ -3855,7 +3855,7 @@ required_summary_terms = ["origin=previous-review-miss", 
"current=change-request
 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-coordination", statements = ["Give 
every fresh pair the current requirements and their provenance, complete 
candidate inventory and relevant raw evidence without the author's full session 
history, earlier assessments, candidate lists or dispositions, a target 
conclusion or an expected finding count.", "Before changing an overall 
conclusion, compare the prior and current effective candidates, requirement 
sources and versions, applicable exceptions [...]
+policy_assertions = [{source = "review-pr-coordination", statements = ["Give 
every independent reviewer the current requirements and their provenance, 
complete candidate inventory and relevant raw evidence without the author's 
full session history, earlier assessments, candidate lists or dispositions, a 
target conclusion or an expected finding count.", "Before changing an overall 
conclusion, compare the prior and current effective candidates, requirement 
sources and versions, applicable  [...]
 
 [[cases]]
 id = "review_ledger_reuses_facts_without_inheriting_completion"
@@ -4421,7 +4421,7 @@ required_summary_term_groups = [["unrelated_rows", 
"query"]]
 forbidden_summary_terms = ["A.status=keep", "B.status=change", 
"note.origin=confirmed-requirement", "query.status=keep", "packet=Q", 
"A.unrequired=[]", "B.unrequired=[FormatHook,Formatter.hooks]"]
 critical = true
 policy_binding_profile = "review-pr"
-policy_assertions = [{source = "review-pr", statements = ["Do not promote an 
unverified inference into a requirement for a new interface, SPI, behavior, or 
test.", "Reconstruct those requirements from original user requests and 
maintained contracts before checking the author's proposed solution, 
distinguishing author choices and unverified assumptions from confirmed 
constraints."]}, {source = "review-pr-coordination", statements = ["Give every 
fresh pair the current requirements and thei [...]
+policy_assertions = [{source = "review-pr", statements = ["Do not promote an 
unverified inference into a requirement for a new interface, SPI, behavior, or 
test.", "Reconstruct those requirements from original user requests and 
maintained contracts before checking the author's proposed solution, 
distinguishing author choices and unverified assumptions from confirmed 
constraints."]}, {source = "review-pr-coordination", statements = ["Give every 
independent reviewer the current requirement [...]
 
 [[cases]]
 id = "review_pair_isolation_and_single_pass_roles"
@@ -4441,7 +4441,7 @@ required_summary_terms = 
["A=two-independent-assessments", "B=one-complete-workf
 forbidden_summary_terms = ["A=one-assessment", "B=start-another-pair", 
"packet=Q", "C=repeat-verification", "D=inherit-prior-completion", 
"E=claim-two-independent-reviews", "F=skip-pair-for-reply-mode", 
"G=expand-to-full-candidate-pair"]
 critical = true
 policy_binding_profile = "review-pr"
-policy_assertions = [{source = "review-pr-coordination", statements = ["For 
every top-level Formal Review and every discussion reply that makes or changes 
an overall readiness conclusion, a coordinator must obtain two independent 
complete assessments of the same effective candidate, original baseline, 
authoritative scope, requirements and rule versions, and review focus.", "A 
reply limited to one finding remains scoped to that finding and does not 
require a full-candidate pair unless it  [...]
+policy_assertions = [{source = "review-pr-coordination", statements = ["For 
every top-level Formal Review and every discussion reply that makes or changes 
an overall readiness conclusion, a coordinator must first obtain two mutually 
blind independent complete assessments of the same effective candidate, 
original baseline, authoritative scope, requirements and rule versions, and 
review focus.", "A reply limited to one finding remains scoped to that finding 
and does not require a full-cand [...]
 
 [[cases]]
 id = "matching_review_pair_requires_substantive_result_review"
@@ -4451,97 +4451,97 @@ phase = "read-only review-result evidence assessment"
 ordinary_loop = false
 profile = "review-pr"
 semantic_policy_assertions = true
-prompt = "Answer this self-contained read-only policy question using only the 
supplied facts and bound rules; do not inspect files, run commands, edit 
anything or produce a Formal Review report. A: two isolated complete reports 
both say Mergeable and list no findings. The coordinator has not checked their 
evidence. Choose A=review-both-results or A=finish-immediately. B: during that 
result review the complete current source and supported consuming path prove 
that both reports missed a ca [...]
+prompt = "Answer this self-contained read-only policy question using only the 
supplied facts and bound rules; do not inspect files, run commands, edit 
anything or produce a Formal Review report. A: two isolated complete reports 
both say Mergeable and list no findings. The coordinator has not checked their 
evidence. Choose A=review-both-results or A=finish-immediately. B: during that 
result review the complete current source and supported consuming path prove 
that both reports missed a ca [...]
 decision = "proceed"
 required_actions = []
 allowed_actions = []
 forbidden_actions = ["inspect_local", "edit_code", "edit_non_code", 
"run_local_checks", "run_pre_handoff_review", "propose_commit_message", 
"mutate_git", "mutate_remote"]
 required_reasons = ["read_only_request"]
-required_summary_terms = ["A=review-both-results", 
"B=adjudicate-and-obtain-second-pair", "C=different-issue-sets", 
"D=same-normalized-issue-set", "E=retain-confirmed-decision-condition"]
-forbidden_summary_terms = ["A=finish-immediately", 
"B=accept-matching-mergeable", "C=same-issue-set", "D=different-issue-sets", 
"E=invent-code-defect", "E=mergeable"]
+required_summary_terms = ["A=review-both-results", 
"B=adjudicate-and-complete", "C=different-issue-sets", 
"D=same-normalized-issue-set", "E=retain-confirmed-decision-condition"]
+forbidden_summary_terms = ["A=finish-immediately", "B=obtain-second-pair", 
"B=accept-matching-mergeable", "C=same-issue-set", "D=different-issue-sets", 
"E=invent-code-defect", "E=mergeable"]
 critical = true
 policy_binding_profile = "review-pr"
-policy_assertions = [{source = "review-pr-coordination", statements = ["The 
coordinator must substantively review both assessments and their supporting 
evidence even when their verdicts and issue lists already match.", "Recheck 
each claim's full path, strongest counter-evidence, necessity, scope and 
decision classification, and check the pair for duplicated issues, omitted 
historical candidates and unsupported acceptance conclusions.", "Normalize 
issues by root cause, trigger and indepen [...]
+policy_assertions = [{source = "review-pr-coordination", statements = ["The 
coordinator must substantively review every assessment and its supporting 
evidence even when the verdicts and issue lists already match.", "Recheck each 
claim's full path, strongest counter-evidence, necessity, scope and decision 
classification, and check the cumulative reports for duplicated issues, omitted 
historical candidates and unsupported acceptance conclusions.", "Normalize 
issues by root cause, trigger a [...]
 
 [[cases]]
 id = "review_pair_preserves_historical_candidate_dispositions"
 group = "review-pr independent review coordination"
-description = "Rejects agreement that omits a proven historical issue and 
permits evidence-based withdrawal or deduplication."
+description = "Preserves every historical candidate in the verified overall 
ledger despite later omissions and permits evidence-based withdrawal or 
deduplication."
 phase = "read-only historical-review-candidate assessment"
 ordinary_loop = false
 profile = "review-pr"
 semantic_policy_assertions = true
-prompt = "Answer this self-contained read-only policy question using only the 
supplied facts and bound rules; do not inspect files, run commands, edit 
anything or produce a Formal Review report. The basis is unchanged. The first 
pair reports respectively issues {A} and {A,B}; a second pair independently 
reports {A} and {A}. Every original report is preserved. Scenario P: current 
full-path evidence and strongest counter-evidence prove that B is a separate 
candidate-introduced defect requi [...]
+prompt = "Answer this self-contained read-only policy question using only the 
supplied facts and bound rules; do not inspect files, run commands, edit 
anything or produce a Formal Review report. The basis is unchanged. The initial 
two reports respectively list issues {A} and {A,B}; the next single independent 
report lists only {A}. Every original report is preserved, and complete 
current-basis coverage and required verification are established. Scenario P: 
current full-path evidence and  [...]
 decision = "proceed"
 required_actions = []
 allowed_actions = []
 forbidden_actions = ["inspect_local", "edit_code", "edit_non_code", 
"run_local_checks", "run_pre_handoff_review", "propose_commit_message", 
"mutate_git", "mutate_remote"]
 required_reasons = ["read_only_request"]
-required_summary_terms = ["P=continue-fresh-review", "Q=accept-latest-pair", 
"R=merge-duplicate", "S=reject-mixed-bases"]
-forbidden_summary_terms = ["P=finish-with-A", "P=not-converged", 
"Q=require-identical-historical-counts", "R=retain-two-blockers", 
"S=reuse-earlier-pair"]
+required_summary_terms = ["P=complete-with-A-and-B", 
"Q=complete-with-supported-withdrawal", "R=merge-duplicate", 
"S=reject-mixed-bases"]
+forbidden_summary_terms = ["P=continue-fresh-review", "P=finish-with-A", 
"P=not-converged", "Q=require-identical-historical-counts", 
"R=retain-two-blockers", "S=reuse-earlier-pair"]
 critical = true
 policy_binding_profile = "review-pr"
-policy_assertions = [{source = "review-pr-coordination", statements = 
["Preserve every original assessment and ledger without rewriting them to make 
the pair agree.", "Record an evidence-based disposition for every candidate 
raised in any assessment, including retained, withdrawn or merged candidates, 
with its supporting evidence, strongest counter-evidence and explanation.", "A 
later reviewer failing to rediscover a candidate does not withdraw it, and 
neither majority voting nor repeate [...]
+policy_assertions = [{source = "review-pr-coordination", statements = 
["Preserve every original assessment and ledger without rewriting them to agree 
with the overall adjudication.", "Record an evidence-based disposition for 
every candidate raised in any assessment, including retained, withdrawn or 
merged candidates, with its supporting evidence, strongest counter-evidence and 
explanation.", "A later reviewer failing to rediscover a candidate does not 
withdraw it, and neither majority vo [...]
 
 [[cases]]
 id = "review_pair_limit_is_not_missing_evidence"
 group = "review-pr independent review coordination"
-description = "Allows a third pair but stops at six assessments without 
misclassifying exhaustion as unavailable evidence."
+description = "Limits each unchanged basis to six assessments in five stages 
and distinguishes unresolved exhaustion from missing decisive evidence."
 phase = "read-only review-convergence termination assessment"
 ordinary_loop = false
 profile = "review-pr"
 semantic_policy_assertions = true
-prompt = "Answer this self-contained read-only policy question using only the 
supplied facts and bound rules; do not inspect files, run commands, edit 
anything or produce a Formal Review report. A: four complete independent 
reviews on one unchanged basis form two pairs. The first pair disagrees and the 
second pair still disagrees after substantive result review. No decisive fact 
is unavailable, and further isolated reviews are available. Choose 
A=continue-third-round or A=stop-not-conver [...]
+prompt = "Answer this self-contained read-only policy question using only the 
supplied facts and bound rules; do not inspect files, run commands, edit 
anything or produce a Formal Review report. A: four complete independent 
assessments on one unchanged basis form three stages: an initial pair followed 
by two single reports. After substantive result review, the overall ledger 
still contains an outcome-sensitive candidate requiring adjudication. No 
decisive fact is unavailable, and further [...]
 decision = "proceed"
 required_actions = []
 allowed_actions = []
 forbidden_actions = ["inspect_local", "edit_code", "edit_non_code", 
"run_local_checks", "run_pre_handoff_review", "propose_commit_message", 
"mutate_git", "mutate_remote"]
 required_reasons = ["read_only_request"]
-required_summary_terms = ["A=continue-third-round", 
"A_verdict=no-formal-result", "B=adjudicate-and-continue", 
"C=review-incomplete", "D=report-missing-scope", "E=stop-not-converged", 
"E_verdict=no-formal-result", "F=complete", "G=exclude-coordinator-and-checks"]
-forbidden_summary_terms = ["A=stop-not-converged", 
"A_verdict=review-incomplete", "B=review-incomplete", "C=mergeable", 
"D=claim-completed-dual-review", "E=continue-fourth-round", 
"E_verdict=review-incomplete", "F=stop-not-converged", 
"G=count-coordinator-and-checks"]
+required_summary_terms = ["A=obtain-one-fresh-assessment", 
"A_verdict=no-formal-result", "B=adjudicate-and-continue", 
"C=review-incomplete", "D=report-missing-scope", "E=stop-not-converged", 
"E_verdict=no-formal-result", "F=complete", "G=exclude-coordinator-and-checks"]
+forbidden_summary_terms = ["A=stop-not-converged", 
"A_verdict=review-incomplete", "B=review-incomplete", "C=mergeable", 
"D=claim-completed-dual-review", "E=obtain-seventh-assessment", 
"E_verdict=review-incomplete", "F=stop-not-converged", 
"G=count-coordinator-and-checks"]
 critical = true
 policy_binding_profile = "review-pr"
-policy_assertions = [{source = "review-pr-coordination", statements = ["Permit 
at most three pairs and six complete independent assessments for one unchanged 
review basis.", "Coordinator result reviews, evidence checks and verification 
reports do not count toward this limit.", "Do not select an arbitrary matching 
pair across rounds or impose an unagreed round or time limit.", "Do not extend 
the six-assessment limit without explicit user authorization.", "Otherwise 
verify the differences, [...]
+policy_assertions = [{source = "review-pr-coordination", statements = ["Permit 
at most six complete independent assessments in at most five stages for one 
unchanged review basis: two in the first stage and one in each subsequent 
stage.", "Coordinator result reviews, evidence checks and verification reports 
do not count toward this limit.", "If confirmation remains unfinished, verify 
available evidence and obtain only one fresh independent complete assessment at 
a time while fewer than si [...]
 
 [[cases]]
 id = "review_confirmation_requires_fresh_adjacent_agreement"
 group = "review-pr independent review coordination"
-description = "Requires a second fresh round and complete adjacent agreement 
instead of adjudicator-only or nonadjacent matches."
+description = "Completes a verified cumulative assessment without report 
equality or consecutive stability while preserving unresolved candidates."
 phase = "read-only cross-round confirmation assessment"
 ordinary_loop = false
 profile = "review-pr"
 semantic_policy_assertions = true
-prompt = "Answer this self-contained read-only policy question using only the 
supplied facts and bound rules; do not inspect files, run commands, edit 
anything or produce a Formal Review report. The following states are 
independent reviews on unchanged established bases, with complete supported 
evidence and historical dispositions except for the stated differences. A: the 
first fresh pair and its substantive adjudication all agree on Mergeable. 
Choose A=continue or A=complete. B: the sec [...]
+prompt = "Answer this self-contained read-only policy question using only the 
supplied facts and bound rules; do not inspect files, run commands, edit 
anything or produce a Formal Review report. The following states are 
independent reviews on unchanged established bases. Complete current-basis 
coverage, required verification and historical dispositions are established 
except for the stated unresolved candidates. A: the initial pair and 
substantive overall adjudication support Mergeable,  [...]
 decision = "proceed"
 required_actions = []
 allowed_actions = []
 forbidden_actions = ["inspect_local", "edit_code", "edit_non_code", 
"run_local_checks", "run_pre_handoff_review", "propose_commit_message", 
"mutate_git", "mutate_remote"]
 required_reasons = ["read_only_request"]
-required_summary_terms = ["A=continue", "B=complete", "C=continue", 
"D=stop-not-converged", "E=continue", "F=complete"]
-forbidden_summary_terms = ["A=complete", "B=continue", 
"C=complete-on-stable-adjudication", "D=accept-nonadjacent-match", 
"E=accept-matching-counts", "F=continue-on-metadata-change"]
+required_summary_terms = ["A=complete", "B=complete", "C=continue", 
"D=complete", "E=complete", "F=complete", "G=leave-result-fields-empty"]
+forbidden_summary_terms = ["A=continue", "B=require-identical-reports", 
"C=complete-on-stable-adjudication", "D=require-another-report", 
"E=require-stable-classification", "F=continue-on-metadata-change", 
"G=publish-completed-result"]
 critical = true
 policy_binding_profile = "review-pr"
-policy_assertions = [{source = "review-pr-coordination", statements = ["Permit 
at most three pairs and six complete independent assessments for one unchanged 
review basis.", "If the third pair does not satisfy confirmation or a concrete 
failure prevents continuing confirmation, report `Review Status: Not 
Converged`, that reason, the confirmed facts and unresolved differences without 
a formal `Review Result` line or a mergeability conclusion.", "If no previous 
adjudication exists on this  [...]
+policy_assertions = [{source = "review-pr-coordination", statements = ["Permit 
at most six complete independent assessments in at most five stages for one 
unchanged review basis: two in the first stage and one in each subsequent 
stage.", "Confirm the overall assessment as soon as complete current-basis 
coverage and required verification are established, every known candidate has 
an evidence-based disposition, the supporting evidence and counter-evidence are 
sufficient, and no unresolved  [...]
 
 [[cases]]
 id = "review_confirmation_restarts_after_basis_change"
 group = "review-pr independent review coordination"
-description = "Preserves history while requiring two consecutive current-basis 
rounds after an input change."
+description = "Restarts with two current-basis reports after a real input 
change while retaining immutable history and permitting immediate completion."
 phase = "read-only review-basis reset assessment"
 ordinary_loop = false
 profile = "review-pr"
 semantic_policy_assertions = true
-prompt = "Answer this self-contained read-only policy question using only the 
supplied facts and bound rules; do not inspect files, run commands, edit 
anything or produce a Formal Review report. A prior basis has two complete 
confirming rounds. The effective candidate, an applicable requirement or rule 
version, or an outcome-sensitive external fact then changes. A: choose 
A=start-fresh-current-confirmation or A=reuse-old-confirmation. B: one fresh 
pair and its full adjudication have comp [...]
+prompt = "Answer this self-contained read-only policy question using only the 
supplied facts and bound rules; do not inspect files, run commands, edit 
anything or produce a Formal Review report. A prior basis has a completed 
verified overall assessment. The effective candidate, an applicable requirement 
or rule version, or an outcome-sensitive external fact then changes. A: choose 
A=start-fresh-current-confirmation or A=reuse-old-confirmation. B: an initial 
pair has completed on the new  [...]
 decision = "proceed"
 required_actions = []
 allowed_actions = []
 forbidden_actions = ["inspect_local", "edit_code", "edit_non_code", 
"run_local_checks", "run_pre_handoff_review", "propose_commit_message", 
"mutate_git", "mutate_remote"]
 required_reasons = ["read_only_request"]
 required_summary_terms = ["A=start-fresh-current-confirmation", 
"B=continue-current-basis", "C=complete", 
"D=preserve-history-and-reassess-current-evidence"]
-forbidden_summary_terms = ["A=reuse-old-confirmation", 
"B=count-one-old-round", "C=discard-all-history", "D=delete-old-records", 
"D=use-stale-anchors-as-current-proof"]
+forbidden_summary_terms = ["A=reuse-old-confirmation", 
"B=count-one-old-round", "C=require-second-current-stage", 
"C=discard-all-history", "D=delete-old-records", 
"D=use-stale-anchors-as-current-proof"]
 critical = true
 policy_binding_profile = "review-pr"
-policy_assertions = [{source = "review-pr-coordination", statements = ["A 
changed effective candidate, requirement, rule version or outcome-sensitive 
external fact invalidates the affected prior assessments; never combine results 
from different bases to pass this gate.", "Start fresh confirmation on the 
changed basis while retaining the original task baseline, attributed scope, 
historical candidates and original assessments.", "Only consecutive rounds on 
the current basis count toward it [...]
+policy_assertions = [{source = "review-pr-coordination", statements = ["A 
changed effective candidate, requirement, rule version or outcome-sensitive 
external fact invalidates the affected prior assessments; never combine results 
from different bases to pass this gate.", "Start fresh confirmation on the 
changed basis while retaining the original task baseline, attributed scope, 
historical candidates and original assessments.", "Obtain a new initial pair on 
the changed basis; earlier basi [...]
 
 [[cases]]
 id = "review_confirmation_shares_read_only_and_coding_route"
diff --git a/.codex/skills/review-pr/SKILL.md b/.codex/skills/review-pr/SKILL.md
index efd493495df..b63796d27e3 100644
--- a/.codex/skills/review-pr/SKILL.md
+++ b/.codex/skills/review-pr/SKILL.md
@@ -55,8 +55,9 @@ coverage, or convergence. Never use a previous Formal Review 
result as
 evidence or as a conclusion to match. Treat previous public findings only as
 hypotheses whose cited facts must be reverified.
 
-Two reviews with the same effective candidate, requirements, focus, and
-evidence must produce the same canonical assessment. Public-PR and 
local-candidate reviews may resolve different candidates, but they must apply 
the same code-correctness judgment and Formal Review result mapping. A changed 
focus, requirement,
+Completed coordinated reviews with the same effective candidate, requirements, 
focus, and evidence must produce the same canonical overall assessment.
+Individual independent assessments may discover different candidates; the 
coordinator verifies and adjudicates their cumulative findings under 
Independent Review Coordination.
+Public-PR and local-candidate reviews may resolve different candidates, but 
they must apply the same code-correctness judgment and Formal Review result 
mapping. A changed focus, requirement,
 or external fact changes the review basis and may legitimately change the
 assessment. Mergeability or CI evidence may add external-state findings or
 gaps, but it must not change code-correctness findings derived from an 
otherwise
@@ -315,7 +316,7 @@ For each blocking issue include:
 Do not add patch-level changes after selecting Needs Discussion.
 Do not include placeholder headings.
 In `### Coverage`, report the candidate type, reviewed baseline or head, 
authoritative requirements and files accounted for, accepted behavior and 
project-commitment basis, behavior clusters, completed discovery lenses, 
unresolved gaps, and CI scope.
-Also report the completed independent-review pairs, result-review outcome and 
resolution of any differences without exposing private reviewer materials.
+Also report the completed independent assessments and stages, overall 
adjudication and resolution of any differences without exposing private 
reviewer materials.
 For every public PR or PR-backed local candidate, also report the effective 
candidate SHA, each style-verification scope and command, each exit code, every 
covered applicable PR-impact file, and every `Not Applicable` check with its 
basis.
 For a standalone local candidate, identify the task baseline and attributed 
local delta; for a PR-backed candidate, identify authorized local commits, 
index changes, or working-tree changes separately from the public PR state.
 In Code Correctness Review, state that the result is code-scope only and CI 
was not reviewed while distinguishing the completed local style verification 
from CI.
diff --git a/.codex/skills/review-pr/references/review-coordination.md 
b/.codex/skills/review-pr/references/review-coordination.md
index 5f80066800c..9f810a28560 100644
--- a/.codex/skills/review-pr/references/review-coordination.md
+++ b/.codex/skills/review-pr/references/review-coordination.md
@@ -22,24 +22,24 @@ That outer loop alone owns code changes, their verification 
and coding-task comp
 
 ## Independent Reviewer Roles
 
-For every top-level Formal Review and every discussion reply that makes or 
changes an overall readiness conclusion, a coordinator must obtain two 
independent complete assessments of the same effective candidate, original 
baseline, authoritative scope, requirements and rule versions, and review focus.
+For every top-level Formal Review and every discussion reply that makes or 
changes an overall readiness conclusion, a coordinator must first obtain two 
mutually blind independent complete assessments of the same effective 
candidate, original baseline, authoritative scope, requirements and rule 
versions, and review focus.
 A reply limited to one finding remains scoped to that finding and does not 
require a full-candidate pair unless it makes an overall readiness conclusion.
 Reviewers may discover different relevant raw evidence, but shared evidence 
must refer to the same content version and all outcome-sensitive evidence must 
be reconciled before completion.
 Use isolated reviewer contexts within the active task; do not create another 
user-owned task or send repository material to an external review service.
-Give every fresh pair the current requirements and their provenance, complete 
candidate inventory and relevant raw evidence without the author's full session 
history, earlier assessments, candidate lists or dispositions, a target 
conclusion or an expected finding count.
+Give every independent reviewer the current requirements and their provenance, 
complete candidate inventory and relevant raw evidence without the author's 
full session history, earlier assessments, candidate lists or dispositions, a 
target conclusion or an expected finding count.
 Apply the [main Skill](../SKILL.md)'s evidence-reuse rules when sharing 
current raw facts and verification evidence.
 Never share reviewed-file statuses, candidate findings, proof conclusions or 
convergence results between independent reviewers.
 If isolated contexts are unavailable, report that the required independent 
reviews could not be completed and do not claim a completed Formal Review.
 
 Each independent reviewer runs the complete Review Workflow and Completion 
Gate once, records a separate ledger and original assessment, and returns them 
privately to the coordinator.
 The independent-review role must not start another pair of reviews or publish 
a final Formal Review result.
-Existing previous-miss, challenged-finding and high-risk triggers remain 
applicable within each assessment; the required pair supplies their independent 
review rather than adding another reviewer.
-Preserve every original assessment and ledger without rewriting them to make 
the pair agree.
+Existing previous-miss, challenged-finding and high-risk triggers remain 
applicable within each assessment; this coordination procedure supplies their 
independent review rather than adding another reviewer.
+Preserve every original assessment and ledger without rewriting them to agree 
with the overall adjudication.
 
 ## Review of the Reports
 
-The coordinator must substantively review both assessments and their 
supporting evidence even when their verdicts and issue lists already match.
-Recheck each claim's full path, strongest counter-evidence, necessity, scope 
and decision classification, and check the pair for duplicated issues, omitted 
historical candidates and unsupported acceptance conclusions.
+The coordinator must substantively review every assessment and its supporting 
evidence even when the verdicts and issue lists already match.
+Recheck each claim's full path, strongest counter-evidence, necessity, scope 
and decision classification, and check the cumulative reports for duplicated 
issues, omitted historical candidates and unsupported acceptance conclusions.
 Normalize issues by root cause, trigger and independent fix boundary; compare 
the complete issue sets, evidence gaps, discussion conditions, verdicts and 
feedback modes rather than counts, order or wording.
 Record an evidence-based disposition for every candidate raised in any 
assessment, including retained, withdrawn or merged candidates, with its 
supporting evidence, strongest counter-evidence and explanation.
 A later reviewer failing to rediscover a candidate does not withdraw it, and 
neither majority voting nor repeated matching subsets can discard a historical 
candidate.
@@ -47,28 +47,28 @@ The coordinator's result review is focused verification of 
the reports and their
 
 ## Cross-Round Confirmation
 
-Permit at most three pairs and six complete independent assessments for one 
unchanged review basis.
+Permit at most six complete independent assessments in at most five stages for 
one unchanged review basis: two in the first stage and one in each subsequent 
stage.
 Coordinator result reviews, evidence checks and verification reports do not 
count toward this limit.
 
 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.
 A changed effective candidate, requirement, rule version or outcome-sensitive 
external fact invalidates the affected prior assessments; never combine results 
from different bases to pass this gate.
 Start fresh confirmation on the changed basis while retaining the original 
task baseline, attributed scope, historical candidates and original assessments.
-Only consecutive rounds on the current basis count toward its confirmation; 
earlier basis phases remain historical records.
-
-1. Obtain a fresh independent pair under Independent Reviewer Roles and 
complete Review of the Reports.
-2. Append the original reports and the coordinator's complete adjudication for 
this round, including every historical candidate's current disposition.
-3. If no previous adjudication exists on this basis, retain this first 
adjudication as the comparison baseline and start a fresh round; the first 
round cannot complete confirmation.
-4. Compare the previous and current complete adjudications, including every 
canonical issue's identity, status and classification, evidence gaps, 
discussion conditions, result and feedback mode.
-   Compare unchanged decisions semantically; reviewer identities, member 
mappings, file hashes, evidence wording and report ordering do not establish a 
changed conclusion.
-5. Confirm only when the fresh pair's normalized complete assessments agree 
with each other, the previous adjudication and the current adjudication, the 
supporting evidence and counter-evidence are sufficient, and no unresolved 
issue can change the conclusion.
+Obtain a new initial pair on the changed basis; earlier basis phases remain 
historical records and do not count toward its six-assessment limit or current 
coverage.
+
+1. Obtain the initial independent pair under Independent Reviewer Roles and 
complete Review of the Reports.
+2. Record the coordinator's verified overall ledger in the session's 
cumulative `rounds[].findings`, preserving the original reports and every 
historical candidate's current disposition.
+3. Confirm the overall assessment as soon as complete current-basis coverage 
and required verification are established, every known candidate has an 
evidence-based disposition, the supporting evidence and counter-evidence are 
sufficient, and no unresolved issue can change the conclusion.
+   The first stage may complete confirmation; equality between independent 
reports or stability between consecutive adjudications is not required.
    An evidenced need for a maintainer decision may be a confirmed `Needs 
Discussion` condition; it does not establish a code defect.
-6. Otherwise verify the differences, record their evidence-based dispositions 
without changing the original assessments, retain the updated current 
adjudication as the next comparison baseline and start a fresh round only while 
fewer than three pairs have completed on this basis.
+4. If confirmation remains unfinished, verify available evidence and obtain 
only one fresh independent complete assessment at a time while fewer than six 
assessments have completed on this basis.
+5. Substantively review the new report, compare it with the retained overall 
ledger, adjudicate every new or disputed candidate and append the updated 
cumulative adjudication before checking completion again.
+   Compare decisions semantically; reviewer identities, member mappings, file 
hashes, evidence wording and report ordering do not establish a changed 
conclusion.
 
-Any substantive issue, classification, disposition, evidence-gap or readiness 
change requires a subsequent fresh pair within this limit; if none remains, do 
not confirm the changed conclusion.
-The coordinator must retain and compare the prior adjudication even though the 
fresh reviewers must not receive it.
-Do not select an arbitrary matching pair across rounds or impose an unagreed 
round or time limit.
+A newly proved finding or an evidence-based change in status, classification, 
disposition, gap or readiness does not by itself require another assessment 
once the completion conditions hold.
+The coordinator must retain and compare prior adjudications even though fresh 
reviewers must not receive them.
+Do not select an arbitrary matching subset of reports or impose an unagreed 
stage or time limit.
 Do not extend the six-assessment limit without explicit user authorization.
-If the third pair does not satisfy confirmation or a concrete failure prevents 
continuing confirmation, report `Review Status: Not Converged`, that reason, 
the confirmed facts and unresolved differences without a formal `Review Result` 
line or a mergeability conclusion.
+If confirmation remains unfinished after six assessments or a concrete failure 
prevents continuing confirmation, report `Review Status: Not Converged`, that 
reason, the confirmed facts and unresolved differences without a formal `Review 
Result` line or a mergeability conclusion.
 Disagreement, exhausted review attempts or an unavailable isolation mechanism 
do not satisfy the Review Incomplete Proof Gate.
 
 When authoritative scope cannot be established, preserve the Review Incomplete 
Proof Gate's missing-scope evidence route and state explicitly that complete 
independent code reviews could not start.
@@ -81,22 +81,23 @@ Each reviewer writes one private original result JSON 
containing `reviewer`, the
 Use a distinct isolated reviewer identity for every assessment, compute hashes 
from the frozen file bytes, and keep the original result and ledger unchanged 
through session validation.
 Use `Mergeable`, `Not Mergeable` or `Review Incomplete` for `result`, and an 
empty string, `Change Request` or `Needs Discussion` for `feedback_mode` as 
required by the Formal Decision Contract.
 
-The coordinator's private session JSON keeps `reviews` in consecutive pairs 
and one `rounds` entry per pair, using the following record shape:
+The coordinator's private session JSON keeps `reviews` in assessment order and 
one cumulative `rounds` entry per stage, with two reports in the first stage of 
each basis and one in every subsequent stage, using the following record shape:
 
 ```json
 {"reviews": [{"path": "<original-result JSON>", "sha256": "<original-result 
SHA256>"}], "rounds": [{"result": "<this round's adjudicated result>", 
"feedback_mode": "<this round's adjudicated feedback mode>", "report_reviews": 
[{"reviewer": "<identity>", "reason": "<result and evidence recheck>", 
"counter_evidence": "<strongest contrary evidence checked>", "evidence": 
[{"reviewer": "<identity>", "anchor": {}}]}], "findings": [{"id": "<stable 
canonical issue ID>", "members": [{"reviewer":  [...]
 ```
 
-Include a `report_reviews` entry for both reviewers in every pair, even for 
matching empty issue lists, and substantiate the result classification and 
acceptance premises as well as individual findings.
+Include a `report_reviews` entry for every new report in its stage, even for 
an empty issue list, and substantiate the result classification and acceptance 
premises as well as individual findings.
 Replace every example `anchor` with a complete captured real-source anchor 
from that reviewer's ledger, including its object and range hashes; an empty 
object or a free-form citation does not establish evidence.
-Each round's `findings` must cover every original candidate raised up to that 
round, map each reviewer and finding exactly once, and retain earlier canonical 
IDs with their updated evidence-based disposition.
+Each stage's `findings` must cover every original candidate raised up to that 
stage, map each reviewer and finding exactly once, and retain earlier canonical 
IDs with their updated evidence-based disposition.
 Use the existing finding statuses; a duplicate may map to the same canonical 
issue, while a merged earlier canonical ID remains recorded as withdrawn with 
the merge target explained in `reason`.
 Use `classification` to describe the issue type, discussion condition or 
evidence gap in stable text; it is not a new status enumeration.
 An issue discovered during result review may have empty `members` but still 
requires real evidence, a disposition and counter-evidence; an unresolved 
candidate never permits a completed result.
+An intermediate stage containing any finding with `candidate` status must 
leave both `result` and `feedback_mode` empty.
 After a basis change, retain the earlier rounds and original reports for 
historical completeness and verify the current dispositions from evidence valid 
on the current basis.
 Do not treat an earlier basis's frozen evidence as current or require it to 
pass current-candidate freshness checks.
-The final pair's original result, feedback mode, normalized confirmed issues 
and evidence gaps must agree with each other and with both final consecutive 
adjudications and the proposed session result.
-Complete historical dispositions, including their classifications, must also 
be stable between those adjudications.
+The final cumulative adjudication must satisfy the completion conditions, 
contain no unresolved candidate and match the proposed session result and 
feedback mode.
+Individual reports may differ from that final adjudication only with recorded 
evidence-based dispositions that explain each difference.
 
 Before publishing a completed Formal Review result or an overall readiness 
conclusion in a discussion reply, run `scripts/review_ledger.py 
validate-session --session <JSON>` after the coordinator's substantive result 
review.
 The command is read-only and validates records, references and evidence 
freshness; it neither launches reviewers nor proves their isolation or semantic 
correctness.
diff --git a/.codex/skills/review-pr/scripts/review_ledger.py 
b/.codex/skills/review-pr/scripts/review_ledger.py
index 941933711de..a5de373d023 100755
--- a/.codex/skills/review-pr/scripts/review_ledger.py
+++ b/.codex/skills/review-pr/scripts/review_ledger.py
@@ -782,7 +782,8 @@ def review_evidence_versions(ledger: dict[str, Any]) -> 
dict[tuple[str, str, str
     return result
 
 
-def result_review_errors(proof: Any, reviews: dict[str, dict[str, Any]], 
own_reviewer: str = "", current_basis: bool = True) -> list[str]:
+def result_review_errors(proof: Any, reviews: dict[str, dict[str, Any]], 
basis_ledger: dict[str, Any],
+                         source_ledgers: dict[str, dict[str, Any]], 
own_reviewer: str = "", current_basis: bool = True) -> list[str]:
     if not isinstance(proof, dict) or any(not has_text(proof.get(key)) for key 
in ("reason", "counter_evidence")):
         raise RuntimeError("Result review requires an evidence assessment and 
strongest counter-evidence")
     if not isinstance(proof.get("evidence"), list) or not proof["evidence"]:
@@ -800,17 +801,25 @@ def result_review_errors(proof: Any, reviews: dict[str, 
dict[str, Any]], own_rev
         ledger = reviews[reviewer]["ledger"]
         if not current_basis and anchor not in all_anchors(ledger):
             errors.append("Historical result-review evidence is not recorded 
in its original ledger")
-        elif current_basis and capture_anchor(anchor, ledger) != anchor:
-            errors.append("Result-review evidence is stale")
+        elif current_basis:
+            evidence_ledger = basis_ledger
+            current_anchor = anchor
+            if "source" == anchor["side"]:
+                if not any(each["origin"] == anchor["origin"] and each["path"] 
== anchor["path"] for each in ledger["basis"]["sources"]):
+                    raise RuntimeError("Source anchor does not match a 
declared material and origin")
+                evidence_ledger = source_ledgers.get(anchor["origin"], ledger)
+                material_path = next(each["path"] for each in 
evidence_ledger["basis"]["sources"] if each["origin"] == anchor["origin"])
+                current_anchor = {**anchor, "path": material_path}
+            if capture_anchor(current_anchor, evidence_ledger) != 
current_anchor:
+                errors.append("Result-review evidence is stale")
     return errors
 
 
 def validate_session(session: Any) -> list[str]:
-    if not isinstance(session, dict) or not isinstance(session.get("reviews"), 
list) or len(session["reviews"]) < 2 or len(session["reviews"]) % 2:
-        raise RuntimeError("A review session requires complete pairs of 
original results")
-    if not isinstance(session.get("rounds"), list) or len(session["rounds"]) * 
2 != len(session["reviews"]):
-        raise RuntimeError("Every review pair requires its own substantive 
result review")
-    validate_review_decision(session)
+    if not isinstance(session, dict) or not isinstance(session.get("reviews"), 
list) or len(session["reviews"]) < 2:
+        raise RuntimeError("A review session requires at least two original 
results")
+    if not isinstance(session.get("rounds"), list) or not session["rounds"]:
+        raise RuntimeError("A review session requires substantive 
result-review stages")
     records = [read_review_record(each) for each in session["reviews"]]
     for key in ("reviewer", "path", "ledger_file"):
         if len(records) != len({each[key] for each in records}):
@@ -819,49 +828,66 @@ def validate_session(session: Any) -> list[str]:
     if len({each["ledger"]["basis"]["baseline"]["tree"] for each in records}) 
!= 1:
         errors.append("The original task baseline changed across review 
rounds")
     current_start = 0
+    current_review_start = 0
+    review_offset = 0
+    basis_assessments = 0
     previous_identity = None
     source_versions = {}
-    for index in range(len(session["rounds"])):
-        pair = records[index * 2:index * 2 + 2]
-        identity = review_input_identity(pair[0]["ledger"])
-        if identity != review_input_identity(pair[1]["ledger"]):
-            errors.append("Review candidates, original baselines, requirements 
or focus differ within a pair")
-        pair_versions = {}
-        for record in pair:
+    stage_records = []
+    for index, review_round in enumerate(session["rounds"]):
+        if not isinstance(review_round, dict) or not 
isinstance(review_round.get("report_reviews"), list) or not 
isinstance(review_round.get("findings"), list):
+            raise RuntimeError("Every stage requires report reviews and 
explicit finding dispositions")
+        report_count = len(review_round["report_reviews"])
+        if report_count not in {1, 2}:
+            raise RuntimeError("Every review stage requires one or two 
original reports")
+        stage = records[review_offset:review_offset + report_count]
+        if len(stage) != report_count:
+            raise RuntimeError("Review stages must account for every original 
result exactly once")
+        stage_records.append(stage)
+        identity = review_input_identity(stage[0]["ledger"])
+        if any(identity != review_input_identity(each["ledger"]) for each in 
stage[1:]):
+            errors.append("Review candidates, original baselines, requirements 
or focus differ within a stage")
+        stage_versions = {}
+        for record in stage:
             versions = review_evidence_versions(record["ledger"])
-            if any(key in pair_versions and pair_versions[key] != value for 
key, value in versions.items()):
+            if any(key in stage_versions and stage_versions[key] != value for 
key, value in versions.items()):
                 errors.append("Independent reviews disagree on a shared source 
version")
-            pair_versions.update(versions)
-        if identity != previous_identity or any(key in source_versions and 
source_versions[key] != value for key, value in pair_versions.items()):
+            stage_versions.update(versions)
+        if identity != previous_identity or any(key in source_versions and 
source_versions[key] != value for key, value in stage_versions.items()):
+            if report_count != 2:
+                errors.append("Each review basis requires an initial pair of 
independent original assessments")
             current_start = index
+            current_review_start = review_offset
+            basis_assessments = 0
             source_versions = {}
-        source_versions.update(pair_versions)
+        source_versions.update(stage_versions)
+        basis_assessments += report_count
+        if basis_assessments > 6:
+            errors.append("The current review basis exceeds six independent 
original assessments")
         previous_identity = identity
-    current_pairs = len(session["rounds"]) - current_start
-    if current_pairs > 3:
-        errors.append("The current review basis exceeds six independent 
original assessments")
-    if current_pairs < 2:
-        errors.append("The current review basis requires at least two 
consecutive independent pairs")
+        review_offset += report_count
+    if review_offset != len(records):
+        raise RuntimeError("Review stages must account for every original 
result exactly once")
     for index, record in enumerate(records):
         ledger = record["ledger"]
-        errors.extend(_validate_ledger(ledger, incomplete_result="Review 
Incomplete" == record["result"], current_basis=index >= current_start * 2))
+        errors.extend(_validate_ledger(ledger, incomplete_result="Review 
Incomplete" == record["result"], current_basis=index >= current_review_start))
         confirmed = any(each["status"] == "confirmed" for each in 
ledger["findings"])
         if "Review Incomplete" != record["result"] and confirmed != ("Not 
Mergeable" == record["result"]):
             errors.append("An original decision contradicts its confirmed 
finding set")
+    basis_ledger = records[current_review_start]["ledger"]
+    source_ledgers = {source["origin"]: record["ledger"] for record in 
records[current_review_start:]
+                      for source in record["ledger"]["basis"]["sources"]}
     known_reviews = {}
     original_findings = set()
     prior_ids = set()
     final_groups = []
-    final_members = {}
-    assessments = []
     for index, review_round in enumerate(session["rounds"]):
-        if not isinstance(review_round, dict) or not 
isinstance(review_round.get("report_reviews"), list) or not 
isinstance(review_round.get("findings"), list):
-            raise RuntimeError("Every pair requires report reviews and 
explicit finding dispositions")
-        validate_review_decision(review_round)
         current_basis = index >= current_start
-        pair = records[index * 2:index * 2 + 2]
-        known_reviews.update((each["reviewer"], each) for each in pair)
-        for record in pair:
+        stage = stage_records[index]
+        if index > current_start and len(stage) != 1:
+            errors.append("Later stages on an unchanged review basis require 
one independent original assessment")
+        known_reviews.update((each["reviewer"], each) for each in stage)
+        for record in stage:
             original_findings.update((record["reviewer"], each["id"]) for each 
in record["ledger"]["findings"])
         audited_reviewers = []
         for report_review in review_round["report_reviews"]:
@@ -869,9 +895,10 @@ def validate_session(session: Any) -> list[str]:
                 raise RuntimeError("Every report review requires its original 
reviewer identity")
             reviewer = report_review["reviewer"]
             audited_reviewers.append(reviewer)
-            errors.extend(result_review_errors(report_review, known_reviews, 
own_reviewer=reviewer, current_basis=current_basis))
-        if Counter(audited_reviewers) != Counter(each["reviewer"] for each in 
pair):
-            errors.append("Both original reports require substantive result 
review, even when they agree")
+            errors.extend(result_review_errors(report_review, known_reviews, 
basis_ledger, source_ledgers,
+                                                own_reviewer=reviewer, 
current_basis=current_basis))
+        if Counter(audited_reviewers) != Counter(each["reviewer"] for each in 
stage):
+            errors.append("Every original report requires substantive result 
review in its stage")
         groups, members = {}, {}
         for finding in review_round["findings"]:
             if (not isinstance(finding, dict) or not 
has_text(finding.get("id")) or not has_text(finding.get("classification"))
@@ -882,7 +909,7 @@ def validate_session(session: Any) -> list[str]:
             if finding["id"] in groups:
                 errors.append("Canonical finding identities are duplicated")
             groups[finding["id"]] = finding
-            errors.extend(result_review_errors(finding, known_reviews, 
current_basis=current_basis))
+            errors.extend(result_review_errors(finding, known_reviews, 
basis_ledger, source_ledgers, current_basis=current_basis))
             for member in finding["members"]:
                 if not isinstance(member, dict) or any(not 
has_text(member.get(key)) for key in ("reviewer", "finding")):
                     raise RuntimeError("Finding mappings require an original 
reviewer and finding identity")
@@ -895,30 +922,23 @@ def validate_session(session: Any) -> list[str]:
         if prior_ids - groups.keys():
             errors.append("A previous result-review finding disappeared 
without a disposition")
         prior_ids = set(groups)
-        final_groups, final_members = list(groups.values()), members
-        confirmed = {each["id"] for each in final_groups if each["status"] == 
"confirmed"}
-        gaps = {each["id"] for each in final_groups if each["status"] == 
"review-incomplete-gap"}
-        expected_result = "Review Incomplete" if gaps else "Not Mergeable" if 
confirmed else "Mergeable"
-        if review_round["result"] != expected_result:
-            errors.append("A round decision contradicts its complete 
historical dispositions")
-        assessments.append((review_round["result"], 
review_round["feedback_mode"],
-                            frozenset((each["id"], each["status"], 
each["classification"]) for each in final_groups)))
+        final_groups = list(groups.values())
+        if any(each["status"] == "candidate" for each in final_groups):
+            if (review_round.get("result"), review_round.get("feedback_mode")) 
!= ("", ""):
+                errors.append("Unresolved result-review candidates require an 
empty result and feedback mode")
+        else:
+            validate_review_decision(review_round)
+            confirmed = any(each["status"] == "confirmed" for each in 
final_groups)
+            gaps = any(each["status"] == "review-incomplete-gap" for each in 
final_groups)
+            expected_result = "Review Incomplete" if gaps else "Not Mergeable" 
if confirmed else "Mergeable"
+            if review_round["result"] != expected_result:
+                errors.append("A round decision contradicts its complete 
historical dispositions")
     if any(each["status"] == "candidate" for each in final_groups):
         errors.append("Result-review candidates remain unresolved")
-    if current_pairs >= 2 and assessments[-2] != assessments[-1]:
-        action = "obtain a fresh independent pair" if current_pairs < 3 else 
"stop and report Review Status: Not Converged"
-        errors.append("The latest complete adjudication differs from the 
previous round; " + action)
-    if (session["result"], session["feedback_mode"]) != assessments[-1][:2]:
-        errors.append("The final decision contradicts the latest complete 
adjudication")
-    expected = (session["result"], session["feedback_mode"], confirmed, gaps)
-    expected_dispositions = {(each["id"], each["status"]) for each in 
final_groups}
-    for record in records[-2:]:
-        dispositions = {(final_members[(record["reviewer"], each["id"])], 
each["status"]) for each in record["ledger"]["findings"]
-                        if (record["reviewer"], each["id"]) in final_members}
-        finding_sets = [{finding_id for finding_id, finding_status in 
dispositions if finding_status == status}
-                        for status in ("confirmed", "review-incomplete-gap")]
-        if (record["result"], record["feedback_mode"], *finding_sets) != 
expected or not dispositions.issubset(expected_dispositions):
-            errors.append("The latest pair does not agree with the result 
review and all historical findings")
+    else:
+        validate_review_decision(session)
+        if (session["result"], session["feedback_mode"]) != 
(session["rounds"][-1]["result"], session["rounds"][-1]["feedback_mode"]):
+            errors.append("The final decision contradicts the latest complete 
adjudication")
     return errors
 
 
@@ -928,7 +948,7 @@ def cmd_validate_session(args: argparse.Namespace) -> int:
         for error in errors:
             print(f"ERROR: {error}")
         return 1
-    print("OK: review pair and substantive result-review records are complete; 
semantic judgment remains the reviewer's responsibility")
+    print("OK: review stages and substantive result-review records are 
complete; semantic judgment remains the reviewer's responsibility")
     return 0
 
 
diff --git a/.codex/skills/review-pr/tests/test_review_ledger.py 
b/.codex/skills/review-pr/tests/test_review_ledger.py
index 2868e593c89..75e0d6e8a76 100644
--- a/.codex/skills/review-pr/tests/test_review_ledger.py
+++ b/.codex/skills/review-pr/tests/test_review_ledger.py
@@ -643,7 +643,7 @@ class ReviewLedgerTest(unittest.TestCase):
                     report_reviews[0]["evidence"][0]["anchor"]["end_line"] = 99
                 else:
                     report_reviews[0][missing] = [] if "evidence" == missing 
else " "
-                if missing in {"reason", "counter_evidence", "evidence", 
"valid_range"}:
+                if missing in {"report", "reason", "counter_evidence", 
"evidence", "valid_range"}:
                     self.assert_session_cli_rejected(actual)
                 else:
                     self.assertTrue(ledger.validate_session(actual))
@@ -702,15 +702,13 @@ class ReviewLedgerTest(unittest.TestCase):
                     actual["rounds"][-1]["findings"][0]["status"] = "candidate"
                 self.assertTrue(ledger.validate_session(actual))
 
-    def 
test_session_rejects_same_count_with_different_findings_or_feedback(self):
+    def test_session_accepts_different_independent_findings_and_feedback(self):
         actual = self.create_session([[("F1", "confirmed")], [("F2", 
"confirmed")]])
-        self.assertTrue(ledger.validate_session(actual))
-        for review_round in actual["rounds"]:
-            
review_round["findings"][0]["members"].extend(review_round["findings"].pop()["members"])
+        self.assertEqual([], ledger.validate_session(actual))
         report = 
json.loads(Path(actual["reviews"][-1]["path"]).read_text(encoding="utf-8"))
         report["feedback_mode"] = "Needs Discussion"
         self.save_session_report(actual, len(actual["reviews"]) - 1, report)
-        self.assertTrue(ledger.validate_session(actual))
+        self.assertEqual([], ledger.validate_session(actual))
 
     def test_session_preserves_finding_discovered_during_result_review(self):
         original = self.create_session([[], [], [("F1", "confirmed")], [("F1", 
"confirmed")]])
@@ -723,59 +721,63 @@ class ReviewLedgerTest(unittest.TestCase):
         })
         original["rounds"][0].update({"result": "Not Mergeable", 
"feedback_mode": "Change Request"})
         first_pair = json.loads(json.dumps(original))
-        first_pair.update({"reviews": first_pair["reviews"][:2], "rounds": 
first_pair["rounds"][:1], "result": "Mergeable", "feedback_mode": ""})
-        self.assertTrue(ledger.validate_session(first_pair))
+        first_pair.update({"reviews": first_pair["reviews"][:2], "rounds": 
first_pair["rounds"][:1],
+                           "result": "Not Mergeable", "feedback_mode": "Change 
Request"})
+        self.assertEqual([], ledger.validate_session(first_pair))
         missing_identity = json.loads(json.dumps(original))
         missing_identity["rounds"][1]["findings"][0]["id"] = "replacement-F1"
         self.assertTrue(ledger.validate_session(missing_identity))
         self.assertEqual([], ledger.validate_session(original))
 
-    def test_session_requires_historical_findings_in_final_pair(self):
+    def 
test_session_retains_historical_findings_omitted_by_later_reports(self):
         original = self.create_session([[("F1", "confirmed")], [("F1", 
"confirmed"), ("F2", "confirmed")],
                                         [("F1", "confirmed")], [("F1", 
"confirmed")],
                                         [("F1", "confirmed")], [("F1", 
"confirmed")]])
-        self.assertTrue(ledger.validate_session(original))
+        self.assertEqual([], ledger.validate_session(original))
         actual = json.loads(json.dumps(original))
         actual["rounds"][-1]["findings"].pop()
         self.assertTrue(ledger.validate_session(actual))
         actual = json.loads(json.dumps(original))
-        for review_round in actual["rounds"][-2:]:
-            review_round["findings"][-1]["status"] = "withdrawn"
+        actual["rounds"][-1]["findings"][-1].update({
+            "status": "withdrawn", "reason": "Current source evidence 
disproves the historical triggering path.",
+            "evidence": actual["rounds"][-1]["report_reviews"][0]["evidence"],
+        })
         self.assertEqual([], ledger.validate_session(actual))
 
-    def test_session_allows_more_than_two_complete_pairs(self):
-        actual = self.create_session([[("F1", "confirmed")], [], [("F1", 
"confirmed")], [("F1", "confirmed")],
-                                      [("F1", "confirmed")], [("F1", 
"confirmed")]])
-        actual["rounds"][-1]["findings"][0].update({"reason": "The fresh 
source evidence confirms the same triggering path.",
-                                                   "evidence": 
actual["rounds"][-1]["report_reviews"][0]["evidence"]})
-        self.assertEqual([], ledger.validate_session(actual))
+    def test_session_accepts_two_through_six_original_assessments(self):
+        for count in range(2, 7):
+            with self.subTest(count=count):
+                actual = self.create_session([[("F1", "confirmed")]] + [[]] * 
(count - 1))
+                actual["rounds"][-1]["findings"][0].update({"reason": "The 
fresh source evidence confirms the same triggering path.",
+                                                           "evidence": 
actual["rounds"][-1]["report_reviews"][0]["evidence"]})
+                self.assertEqual([], ledger.validate_session(actual))
 
-    def test_session_rejects_more_than_three_complete_pairs(self):
-        actual = self.create_session([[]] * 8)
+    def test_session_rejects_seventh_original_assessment(self):
+        actual = self.create_session([[]] * 7)
         self.assertIn("The current review basis exceeds six independent 
original assessments", ledger.validate_session(actual))
 
-    def test_session_stops_after_third_unconfirmed_pair(self):
-        actual = self.create_session([[("F1", "confirmed")]] * 6)
-        actual["rounds"][-1]["findings"][0]["classification"] = "performance"
-        self.assertIn("The latest complete adjudication differs from the 
previous round; stop and report Review Status: Not Converged", 
ledger.validate_session(actual))
-
-    def test_session_requires_confirmation_after_first_round(self):
-        actual = self.create_session([[], []])
-        actual.update({"reviews": actual["reviews"][:2], "rounds": 
actual["rounds"][:1]})
-        self.assertTrue(ledger.validate_session(actual))
+    def test_session_accepts_new_confirmed_finding_in_sixth_assessment(self):
+        actual = self.create_session([[], [], [], [], [], [("F1", 
"confirmed")]])
+        self.assertEqual([], ledger.validate_session(actual))
 
-    def test_session_requires_stable_complete_dispositions(self):
+    def test_session_accepts_evidenced_final_disposition_changes(self):
         original = self.create_session([[("F1", "withdrawn")]] * 6)
         for field, value in (("classification", "performance"), ("status", 
"non-blocking")):
             with self.subTest(field=field):
                 actual = json.loads(json.dumps(original))
-                actual["rounds"][-1]["findings"][0][field] = value
-                self.assertTrue(ledger.validate_session(actual))
+                finding = actual["rounds"][-1]["findings"][0]
+                finding.update({field: value, "reason": "The current owning 
contract supports this revised disposition.",
+                                "evidence": 
actual["rounds"][-1]["report_reviews"][0]["evidence"]})
+                self.assertEqual([], ledger.validate_session(actual))
 
-    def test_session_requires_stable_feedback_mode(self):
-        actual = self.create_session([[("F1", "confirmed")], [("F1", 
"confirmed")]])
-        actual["rounds"][-2]["feedback_mode"] = "Needs Discussion"
-        self.assertTrue(ledger.validate_session(actual))
+    def test_session_accepts_evidenced_final_feedback_change(self):
+        actual = self.create_session([[("F1", "confirmed")], [("F1", 
"confirmed")], []])
+        actual["rounds"][0]["feedback_mode"] = "Needs Discussion"
+        actual["rounds"][-1]["findings"][0].update({
+            "reason": "Current source evidence establishes a required 
correctness fix.",
+            "evidence": actual["rounds"][-1]["report_reviews"][0]["evidence"],
+        })
+        self.assertEqual([], ledger.validate_session(actual))
 
     def test_session_requires_final_closed_dispositions(self):
         for status in ("withdrawn", "non-blocking", "out-of-scope"):
@@ -784,25 +786,59 @@ class ReviewLedgerTest(unittest.TestCase):
                 for reference in actual["reviews"]:
                     report = 
json.loads(Path(reference["path"]).read_text(encoding="utf-8"))
                     self.assertEqual([], 
ledger.validate_ledger(ledger.read_ledger(report["ledger"])))
-                errors = ledger.validate_session(actual)
-                if "withdrawn" == status:
-                    self.assertEqual([], errors)
-                else:
-                    self.assertIn("The latest pair does not agree with the 
result review and all historical findings", errors)
+                actual["rounds"][-1]["findings"][0].update({
+                    "status": status, "reason": "The current owner contract 
supports the coordinator's final disposition.",
+                    "evidence": 
actual["rounds"][-1]["report_reviews"][0]["evidence"],
+                })
+                self.assertEqual([], ledger.validate_session(actual))
 
-    def test_session_rejects_repeated_unconfirmed_adjudication(self):
-        actual = self.create_session([[("F1", "confirmed")], []])
+    def test_session_rejects_unresolved_final_adjudication(self):
+        actual = self.create_session([[("F1", "confirmed")]] * 6)
+        actual["rounds"][-1]["findings"][0]["status"] = "candidate"
+        actual["rounds"][-1].update({"result": "", "feedback_mode": ""})
         self.assertTrue(ledger.validate_session(actual))
 
-    def test_session_requires_complete_review_pairs(self):
+    def test_session_requires_two_initial_original_assessments(self):
         original = self.create_session([[], []])
-        for count in (0, 1, 3):
+        for count in (0, 1):
             with self.subTest(count=count):
                 actual = json.loads(json.dumps(original))
                 actual["reviews"] = (actual["reviews"] * 2)[:count]
                 with self.assertRaises(RuntimeError):
                     ledger.validate_session(actual)
 
+    def test_session_requires_complete_initial_and_singleton_stages(self):
+        original = self.create_session([[], [], [], []])
+        for invalid in ("initial_singleton", "later_pair", "missing_stage", 
"duplicate_report", "wrong_stage"):
+            with self.subTest(invalid=invalid):
+                actual = json.loads(json.dumps(original))
+                if "initial_singleton" == invalid:
+                    first_stage = actual["rounds"][0]
+                    second_stage = json.loads(json.dumps(first_stage))
+                    second_stage["report_reviews"] = 
[first_stage["report_reviews"].pop()]
+                    actual["rounds"].insert(1, second_stage)
+                elif "later_pair" == invalid:
+                    
actual["rounds"][1]["report_reviews"].extend(actual["rounds"].pop()["report_reviews"])
+                elif "missing_stage" == invalid:
+                    actual["rounds"].pop()
+                elif "duplicate_report" == invalid:
+                    actual["rounds"][1]["report_reviews"] = 
actual["rounds"][0]["report_reviews"][:1]
+                else:
+                    actual["rounds"][1]["report_reviews"], 
actual["rounds"][2]["report_reviews"] = \
+                        actual["rounds"][2]["report_reviews"], 
actual["rounds"][1]["report_reviews"]
+                self.assert_session_cli_rejected(actual)
+
+    def 
test_session_keeps_intermediate_candidates_without_a_formal_verdict(self):
+        original = self.create_session([[("F1", "confirmed")], [], []])
+        original["rounds"][0]["findings"][0]["status"] = "candidate"
+        original["rounds"][0].update({"result": "", "feedback_mode": ""})
+        self.assertEqual([], ledger.validate_session(original))
+        for result, feedback in (("Not Mergeable", "Change Request"), ("", 
"Change Request")):
+            with self.subTest(result=result, feedback=feedback):
+                actual = json.loads(json.dumps(original))
+                actual["rounds"][0].update({"result": result, "feedback_mode": 
feedback})
+                self.assert_session_cli_rejected(actual)
+
     def test_session_rejects_reused_or_modified_original_records(self):
         original = self.create_session([[], []])
         original_report = 
Path(original["reviews"][1]["path"]).read_text(encoding="utf-8")
@@ -878,7 +914,7 @@ class ReviewLedgerTest(unittest.TestCase):
     def test_session_reconfirms_changed_basis_and_retains_history(self):
         for changed in ("candidate", "evidence"):
             with self.subTest(changed=changed):
-                historical = self.create_session([[("F1", "confirmed")], 
[("F1", "confirmed")]])
+                historical = self.create_session([[("F1", "confirmed")], 
[("F1", "confirmed")], []])
                 if "candidate" == changed:
                     (self.repo / 
self.source_path).write_text("first\nfixed_first\nthird\nfourth\nfifth\nnew_second\nlast\n",
 encoding="utf-8")
                 else:
@@ -891,13 +927,142 @@ class ReviewLedgerTest(unittest.TestCase):
                                         "evidence": 
review_round["report_reviews"][0]["evidence"]})
                 actual = {**current, "reviews": historical["reviews"] + 
current["reviews"],
                           "rounds": historical["rounds"] + current["rounds"]}
-                pending = {**actual, "reviews": actual["reviews"][:-2], 
"rounds": actual["rounds"][:-1]}
-                self.assertTrue(ledger.validate_session(pending))
+                pending = json.loads(json.dumps(actual))
+                pending["reviews"].pop()
+                pending["rounds"][-1]["report_reviews"].pop()
+                self.assertIn("Each review basis requires an initial pair of 
independent original assessments", ledger.validate_session(pending))
                 self.assertEqual([], ledger.validate_session(actual))
+                if "candidate" == changed:
+                    stale = json.loads(json.dumps(actual))
+                    stale["rounds"][-1]["findings"][0]["evidence"] = 
historical["rounds"][-1]["findings"][0]["evidence"]
+                    self.assertTrue(ledger.validate_session(stale))
                 original_report = Path(historical["reviews"][0]["path"])
                 original_report.write_bytes(original_report.read_bytes() + 
b"\n")
                 self.assert_session_cli_rejected(actual)
 
+    def test_session_rejects_exhausted_historical_basis(self):
+        historical = self.create_session([[]] * 7)
+        (self.repo / 
self.source_path).write_text("first\nnext_first\nthird\nfourth\nfifth\nnew_second\nlast\n",
 encoding="utf-8")
+        current = self.create_session([[], []], 
review_start=len(historical["reviews"]))
+        actual = {**current, "reviews": historical["reviews"] + 
current["reviews"],
+                  "rounds": historical["rounds"] + current["rounds"]}
+        self.assertIn("The current review basis exceeds six independent 
original assessments", ledger.validate_session(actual))
+
+    def test_session_rechecks_historical_committed_candidate_proof(self):
+        self.git("add", self.source_path)
+        self.git("commit", "-qm", "Synthetic reviewed candidate")
+        historical = self.create_session([[("F1", "confirmed")], []], 
committed=True)
+        (self.repo / 
self.source_path).write_text("first\nfixed_first\nthird\nfourth\nfifth\nnew_second\nlast\n",
 encoding="utf-8")
+        self.git("add", self.source_path)
+        self.git("commit", "-qm", "Synthetic changed candidate")
+        current = self.create_session([[], []], review_start=2, committed=True)
+        current["rounds"][0].update({"findings": 
json.loads(json.dumps(historical["rounds"][-1]["findings"])),
+                                     "result": "Not Mergeable", 
"feedback_mode": "Change Request"})
+        actual = {**current, "reviews": historical["reviews"] + 
current["reviews"],
+                  "rounds": historical["rounds"] + current["rounds"], 
"result": "Not Mergeable", "feedback_mode": "Change Request"}
+        self.assertIn("Result-review evidence is stale", 
ledger.validate_session(actual))
+        actual["rounds"][-1]["findings"][0]["evidence"] = 
current["rounds"][0]["report_reviews"][0]["evidence"]
+        self.assertEqual([], ledger.validate_session(actual))
+
+    def test_session_reuses_unchanged_candidate_and_base_proof(self):
+        self.git("add", self.source_path)
+        self.git("commit", "-qm", "Synthetic reviewed candidate")
+        historical = self.create_session([[("F1", "confirmed")], []], 
committed=True)
+        report = 
json.loads(Path(historical["reviews"][0]["path"]).read_text(encoding="utf-8"))
+        actual_ledger = ledger.read_ledger(report["ledger"])
+        anchor = ledger.capture_anchor({"side": "base", "path": 
self.source_path, "start_line": 2, "end_line": 2}, actual_ledger)
+        actual_ledger["findings"][0]["anchors"].append(anchor)
+        self.update_session_basis(historical, 0, actual_ledger, 
json.loads(self.basis_file.read_text(encoding="utf-8")))
+        historical["rounds"][0]["findings"][0]["evidence"].append({"reviewer": 
"review-1", "anchor": anchor})
+        (self.repo / "Rules.md").write_text("Use the owning public boundary 
and preserve the original task baseline.\n", encoding="utf-8")
+        self.git("add", "Rules.md")
+        self.git("commit", "-qm", "Synthetic changed rules with unchanged 
candidate evidence")
+        current = self.create_session([[], []], review_start=2, committed=True)
+        current["rounds"][0].update({"findings": 
json.loads(json.dumps(historical["rounds"][-1]["findings"])),
+                                     "result": "Not Mergeable", 
"feedback_mode": "Change Request"})
+        actual = {**current, "reviews": historical["reviews"] + 
current["reviews"],
+                  "rounds": historical["rounds"] + current["rounds"], 
"result": "Not Mergeable", "feedback_mode": "Change Request"}
+        self.assertEqual([], ledger.validate_session(actual))
+
+    def test_session_accepts_historical_pair_stages_after_rule_change(self):
+        historical = self.create_session([[], [], [], []])
+        
historical["rounds"][1]["report_reviews"].extend(historical["rounds"].pop()["report_reviews"])
+        (self.repo / "Rules.md").write_text("Use an initial pair followed by 
one fresh assessment per later stage.\n", encoding="utf-8")
+        current = self.create_session([[], []], review_start=4)
+        actual = {**current, "reviews": historical["reviews"] + 
current["reviews"],
+                  "rounds": historical["rounds"] + current["rounds"]}
+        self.assertEqual([], ledger.validate_session(actual))
+
+    def 
test_session_rechecks_relocated_source_across_current_evidence_sets(self):
+        historical = self.create_session([[("F1", "confirmed")], []])
+        report = 
json.loads(Path(historical["reviews"][0]["path"]).read_text(encoding="utf-8"))
+        actual_ledger = ledger.read_ledger(report["ledger"])
+        anchor = ledger.capture_anchor({"side": "source", "origin": 
"attempt:target-version", "path": str(self.attempt_log)}, actual_ledger)
+        actual_ledger["findings"][0]["anchors"].append(anchor)
+        self.update_session_basis(historical, 0, actual_ledger, 
json.loads(self.basis_file.read_text(encoding="utf-8")))
+        historical["rounds"][0]["findings"][0]["evidence"] = [{"reviewer": 
"review-1", "anchor": anchor}]
+        relocated = self.private_root / "relocated-attempt.log"
+        self.attempt_log.rename(relocated)
+        (self.repo / "Rules.md").write_text("Use declared source origins 
across all current independent assessments.\n", encoding="utf-8")
+        basis = json.loads(self.basis_file.read_text(encoding="utf-8"))
+        next(each for each in basis["sources"] if "attempt:target-version" == 
each["origin"])["path"] = str(relocated)
+        self.basis_file.write_text(json.dumps(basis), encoding="utf-8")
+        for index, changed in enumerate((False, True)):
+            with self.subTest(changed=changed):
+                if changed:
+                    relocated.write_text("The current source disproves the 
original external fact.\n", encoding="utf-8")
+                current = self.create_session([[], []], review_start=2 + index 
* 2)
+                last_report = 
json.loads(Path(current["reviews"][-1]["path"]).read_text(encoding="utf-8"))
+                last_ledger = ledger.read_ledger(last_report["ledger"])
+                independent_basis = {**basis, "sources": [each for each in 
basis["sources"] if "attempt:target-version" != each["origin"]]}
+                self.update_session_basis(current, 1, last_ledger, 
independent_basis)
+                current["rounds"][0].update({"findings": 
json.loads(json.dumps(historical["rounds"][-1]["findings"])),
+                                             "result": "Not Mergeable", 
"feedback_mode": "Change Request"})
+                actual = {**current, "reviews": historical["reviews"] + 
current["reviews"],
+                          "rounds": historical["rounds"] + current["rounds"], 
"result": "Not Mergeable", "feedback_mode": "Change Request"}
+                if changed:
+                    self.assertIn("Result-review evidence is stale", 
ledger.validate_session(actual))
+                else:
+                    self.assertEqual([], ledger.validate_session(actual))
+                    
actual["rounds"][-1]["findings"][0]["evidence"][0]["anchor"]["path"] = 
str(relocated)
+                    self.assert_session_cli_rejected(actual)
+
+    def 
test_session_recaptures_historical_source_absent_from_current_declarations(self):
+        historical = self.create_session([[("F1", "confirmed")], []])
+        report = 
json.loads(Path(historical["reviews"][0]["path"]).read_text(encoding="utf-8"))
+        actual_ledger = ledger.read_ledger(report["ledger"])
+        anchor = ledger.capture_anchor({"side": "source", "origin": 
"attempt:target-version", "path": str(self.attempt_log)}, actual_ledger)
+        actual_ledger["findings"][0]["anchors"].append(anchor)
+        self.update_session_basis(historical, 0, actual_ledger, 
json.loads(self.basis_file.read_text(encoding="utf-8")))
+        historical["rounds"][0]["findings"][0]["evidence"] = [{"reviewer": 
"review-1", "anchor": anchor}]
+        (self.repo / "Rules.md").write_text("Recheck every retained finding 
using available current material.\n", encoding="utf-8")
+        basis = json.loads(self.basis_file.read_text(encoding="utf-8"))
+        basis["sources"] = [each for each in basis["sources"] if 
"attempt:target-version" != each["origin"]]
+        self.basis_file.write_text(json.dumps(basis), encoding="utf-8")
+        current = self.create_session([[], []], review_start=2)
+        current["rounds"][0].update({"findings": 
json.loads(json.dumps(historical["rounds"][-1]["findings"])),
+                                     "result": "Not Mergeable", 
"feedback_mode": "Change Request"})
+        actual = {**current, "reviews": historical["reviews"] + 
current["reviews"],
+                  "rounds": historical["rounds"] + current["rounds"], 
"result": "Not Mergeable", "feedback_mode": "Change Request"}
+        self.assertEqual([], ledger.validate_session(actual))
+        self.attempt_log.write_text("The material no longer supports the 
original finding.\n", encoding="utf-8")
+        self.assertIn("Result-review evidence is stale", 
ledger.validate_session(actual))
+        self.attempt_log.unlink()
+        self.assert_session_cli_rejected(actual)
+
+    def test_session_accepts_current_coordinator_source_proof(self):
+        actual = self.create_session([[], []])
+        report = 
json.loads(Path(actual["reviews"][0]["path"]).read_text(encoding="utf-8"))
+        actual_ledger = ledger.read_ledger(report["ledger"])
+        anchor = ledger.capture_anchor({"side": "source", "origin": 
"attempt:target-version", "path": str(self.attempt_log)}, actual_ledger)
+        actual["rounds"][0]["findings"] = [{
+            "id": "coordinator-candidate", "members": [], "status": 
"withdrawn", "classification": "correctness",
+            "reason": "Current source evidence disproves the suspected 
trigger.",
+            "counter_evidence": "The recorded external fact rules out the 
alternative path.",
+            "evidence": [{"reviewer": "review-1", "anchor": anchor}],
+        }]
+        self.assertEqual([], ledger.validate_session(actual))
+
     def test_session_preserves_real_incomplete_evidence(self):
         actual = self.create_session([[("G1", "review-incomplete-gap")], 
[("G1", "review-incomplete-gap")]])
         self.assertEqual([], ledger.validate_session(actual))
@@ -940,10 +1105,8 @@ class ReviewLedgerTest(unittest.TestCase):
                         redirect_stdout(StringIO()), 
redirect_stderr(StringIO()):
                     self.assertEqual(1, ledger.main())
 
-    def create_session(self, review_findings, review_start=0):
-        if 2 == len(review_findings):
-            review_findings = review_findings * 2
-        original = self.create_valid_ledger()
+    def create_session(self, review_findings, review_start=0, committed=False):
+        original = self.create_valid_ledger(committed)
         actual = {"reviews": [], "rounds": [], "result": "Mergeable", 
"feedback_mode": ""}
         findings = {}
         report_reviews = []
@@ -972,7 +1135,7 @@ class ReviewLedgerTest(unittest.TestCase):
                                                                   "reason": 
"Checked the triggering path and independent fix boundary.",
                                                                   
"counter_evidence": "Checked the strongest supported counterexample.", 
"evidence": evidence})
                 disposition["members"].append({"reviewer": reviewer, 
"finding": finding["id"]})
-            if index % 2:
+            if index >= 1:
                 round_statuses = {finding["status"] for finding in 
findings.values()}
                 result = "Review Incomplete" if "review-incomplete-gap" in 
round_statuses else "Not Mergeable" if "confirmed" in round_statuses else 
"Mergeable"
                 actual["rounds"].append({"report_reviews": report_reviews, 
"findings": json.loads(json.dumps(list(findings.values()))),
@@ -1010,7 +1173,7 @@ class ReviewLedgerTest(unittest.TestCase):
 
     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)]
+                     "--head-ref", self.git("rev-parse", "HEAD").strip() if 
committed else "HEAD", "--review-basis", str(self.basis_file)]
         if not committed:
             arguments.extend(["--candidate-files", str(self.candidate_files)])
         output = StringIO()

Reply via email to