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 1618dec2ef3 Enforce independent paired reviews and substantive result 
reconciliation (#40217)
1618dec2ef3 is described below

commit 1618dec2ef3c8a02c5e6d0d08b381e8754b266fc
Author: Liang Zhang <[email protected]>
AuthorDate: Sun Oct 18 14:31:13 2026 +0800

    Enforce independent paired reviews and substantive result reconciliation 
(#40217)
---
 .codex/harness/agents/cases.toml                   |  84 ++++++-
 .codex/skills/review-pr/SKILL.md                   |  64 ++++-
 .codex/skills/review-pr/scripts/review_ledger.py   | 155 ++++++++++++
 .../skills/review-pr/tests/test_review_ledger.py   | 277 +++++++++++++++++++++
 4 files changed, 573 insertions(+), 7 deletions(-)

diff --git a/.codex/harness/agents/cases.toml b/.codex/harness/agents/cases.toml
index d24ee1a6066..0f8f82aad64 100644
--- a/.codex/harness/agents/cases.toml
+++ b/.codex/harness/agents/cases.toml
@@ -3597,7 +3597,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", 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 [...]
+policy_assertions = [{source = "review-pr", statements = ["Give each reviewer 
the current requirements and their provenance, complete candidate inventory and 
relevant raw evidence without the author's full session history, previous 
assessments, expected verdict or expected finding count.", "Never use a 
previous Formal Review result as\nevidence or as a conclusion to match.", "If 
at least one candidate passes the Finding Proof Gate, use `Not Mergeable`\n   
with `Feedback Mode: Change Requ [...]
 
 [[cases]]
 id = "review_ledger_reuses_facts_without_inheriting_completion"
@@ -4163,4 +4163,84 @@ 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.", "For a proven previous-review miss, contradictory assessments on 
an unchanged basis, a triggered high-risk r [...]
+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.", "Give each reviewer the current requirements and their 
provenance, complete candidate inventory and relevant [...]
+
+[[cases]]
+id = "review_pair_isolation_and_single_pass_roles"
+group = "review-pr independent review coordination"
+description = "Requires isolated complete reviews while preventing recursive 
pairs and conclusion sharing."
+phase = "read-only independent-review coordination 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 top-level Formal Review has an 
unchanged, fully established basis. Choose A=two-independent-assessments or 
A=one-assessment. B is a reviewer assigned one independent assessment in that 
pair; choose B=one-complete-workflow or B=start-another-pair. Packet P contains 
only current requirements with [...]
+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=two-independent-assessments", 
"B=one-complete-workflow", "packet=P", "C=share-raw-evidence", 
"D=exclude-prior-completion", "E=report-unfinished", 
"F=two-independent-assessments", "G=keep-finding-scope"]
+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", 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 makes an over [...]
+
+[[cases]]
+id = "matching_review_pair_requires_substantive_result_review"
+group = "review-pr independent review coordination"
+description = "Rechecks matching reports and compares normalized complete 
issues instead of matching counts."
+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 [...]
+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"]
+critical = true
+policy_binding_profile = "review-pr"
+policy_assertions = [{source = "review-pr", 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 independent fix boun [...]
+
+[[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."
+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 [...]
+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=not-converged", "Q=accept-latest-pair", 
"R=merge-duplicate", "S=reject-mixed-bases"]
+forbidden_summary_terms = ["P=finish-with-A", 
"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", 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 repeated matching su [...]
+
+[[cases]]
+id = "review_pair_limit_is_not_missing_evidence"
+group = "review-pr independent review coordination"
+description = "Stops after two pairs without a false verdict and preserves the 
separate proven-missing-evidence route."
+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. Choose A=stop-not-converged or A=start-fifth-review, and choose 
A_verdict=no-formal-result or  [...]
+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=stop-not-converged", 
"A_verdict=no-formal-result", "B=adjudicate-and-continue", 
"C=review-incomplete", "D=report-missing-scope", "Review Status: Not Converged"]
+forbidden_summary_terms = ["A=start-fifth-review", 
"A_verdict=review-incomplete", "B=review-incomplete", "C=mergeable", 
"D=claim-completed-dual-review"]
+critical = true
+policy_binding_profile = "review-pr"
+policy_assertions = [{source = "review-pr", statements = ["Permit at most two 
pairs and four complete independent assessments for one unchanged review basis, 
and never select an arbitrary matching pair across groups.", "If the second 
pair still disagrees or contradicts a verified historical disposition, report 
`Review Status: Not Converged`, the confirmed facts and unresolved differences 
without a formal `Review Result` line or a mergeability conclusion.", 
"Disagreement, an unavailable i [...]
diff --git a/.codex/skills/review-pr/SKILL.md b/.codex/skills/review-pr/SKILL.md
index b4650d8a4f2..bf16ec110eb 100644
--- a/.codex/skills/review-pr/SKILL.md
+++ b/.codex/skills/review-pr/SKILL.md
@@ -173,6 +173,39 @@ Map every substantive file to the smallest meaningful 
behavior cluster and ident
 
 Triage functional boundaries, ownership and shared contracts, compatibility 
and rollback, test validity, concurrency and performance, security and 
operations, dependencies, packaging, and generated artifacts. Read only the 
triggered sections of [high-risk-review.md](references/high-risk-review.md). 
Also read [sql-parser-review.md](references/sql-parser-review.md) for SQL 
grammar, visitors, parser tests, syntax documentation, dialect behavior, or 
parser baselines.
 
+## Independent Review Coordination
+
+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 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 each reviewer the current requirements and their provenance, complete 
candidate inventory and relevant raw evidence without the author's full session 
history, previous assessments, expected verdict or expected finding count.
+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.
+Share only current raw facts and verification evidence whose inputs and 
applicable coverage remain valid; never share reviewed-file statuses, candidate 
findings, proof conclusions or convergence results between independent 
reviewers.
+Preserve every original assessment and ledger without rewriting them to make 
the pair agree.
+
+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 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.
+The coordinator's result review is focused verification of the reports and 
their evidence, not another complete independent assessment, and mechanical 
session validation never replaces it.
+
+Accept a pair only when both original assessments agree after issue 
normalization and the coordinator's verified dispositions support that same 
complete assessment, including every historical candidate.
+When the first pair disagrees or its matching result is unsupported, 
adjudicate the candidates from current evidence and obtain one further 
independent pair with a fresh complete review of the same basis.
+Do not give that pair the earlier assessments, candidate list, dispositions or 
a target conclusion; provide any newly verified raw evidence without the prior 
conclusions.
+Permit at most two pairs and four complete independent assessments for one 
unchanged review basis, and never select an arbitrary matching pair across 
groups.
+If the second pair still disagrees or contradicts a verified historical 
disposition, report `Review Status: Not Converged`, the confirmed facts and 
unresolved differences without a formal `Review Result` line or a mergeability 
conclusion.
+Disagreement, an unavailable isolation mechanism and exhausted review attempts 
do not satisfy the Review Incomplete Proof Gate.
+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.
+
+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.
+That proven scope failure may return the existing `Review Incomplete` report 
without a session, but must not claim completed dual coverage or a mergeability 
conclusion.
+
 ## Review Workflow
 
 Apply this workflow to the canonical review basis without letting output mode 
or a previous result influence the assessment:
@@ -182,9 +215,6 @@ Reuse only facts and verification results whose 
current-candidate validity is es
 Before repeating evidence acquisition, consult the current ledger's sources, 
block coverage, candidate findings and verification material, and reuse facts 
only while their supporting inputs and applicable coverage remain valid.
 All three discovery lenses may share the same current source material, but 
each must still cover the complete candidate and sharing material does not 
replace the convergence pass.
 
-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.
-Give the independent reviewer the current requirements and their provenance, 
complete candidate inventory and relevant raw evidence rather than the author's 
full session history.
-
 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.
@@ -211,11 +241,12 @@ Apply the Completion Gate to every Formal Review and to a 
discussion reply that
 - Complete the Mandatory Style Verification Gate against the latest effective 
candidate for every public PR or PR-backed local candidate.
 - Leave no unresolved gap that could change the blocker set and require a 
latest-candidate convergence pass with zero new independent candidates.
 - 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.
+- Use `scripts/review_ledger.py` for each independent assessment; 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.
 - Use the existing private system temporary directory and record the exact 
ledger path returned by initialization for this review.
 - 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>`.
+- Record each evidenced `Needs Discussion` condition as a `confirmed` ledger 
issue about the project commitment or required decision, using `origin`, 
`evidence`, `full_path`, `necessity` and `scope_proof` to prove that 
requirement and `fix_boundary` for its specific decision boundary; do not 
invent a code defect.
 - For each newly introduced local override, compatibility exception or 
parallel mechanism, use the existing basis sources and block `requirements`, 
`counter_evidence` and `anchors` to record its constraint origins, verified 
limits of direct reuse and necessity conclusion separately from behavior 
correctness.
 - Keep author-only choices and unverified assumptions as `evidence` sources.
 - Record partial coverage with pending files and keep unproved findings as 
candidates; recording evidence does not establish a completed review.
@@ -225,10 +256,32 @@ Apply the Completion Gate to every Formal Review and to a 
discussion reply that
 - Mutate one ledger sequentially; do not run ledger commands concurrently.
 - Keep ledger data private and remove only the exact current-review ledger.
 - Before returning a completed review result, form a self-contained report and 
automatically run `scripts/review_ledger.py cleanup --ledger <recorded-ledger>` 
for the recorded current-review instance, including Mergeable, Not Mergeable 
and Review Incomplete results.
+- The coordinator retains the owned review ledgers and input materials until 
result review and session validation finish, then applies the exact-path 
cleanup rules to each recorded instance and its owned session materials, 
including an unconverged session.
 - Before cleaning the ledger, remove only temporary input materials created 
and explicitly recorded for this review, preserving declared sources owned by 
the repository, the user or another review.
 - On a handled failure or cancellation, attempt the same cleanup for resources 
whose ownership is established, preserve the original failure and report any 
cleanup failure or remaining resources.
 - Keep the report independent of temporary files and report cleanup failures 
without changing the review assessment.
 
+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.
+Each reviewer writes one private original result JSON containing `reviewer`, 
the exact `ledger` path, `ledger_sha256`, `result` and `feedback_mode` after 
completing its ledger.
+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 has this shape, with two or four 
`reviews` in consecutive pairs and one `rounds` entry per pair:
+
+```json
+{"reviews": [{"path": "<original-result JSON>", "sha256": "<original-result 
SHA256>"}], "rounds": [{"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": 
"<identity>", "finding": "<ledger finding ID>"}], "status": "<finding status>", 
"reason": "<disposition and  [...]
+```
+
+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.
+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.
+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`.
+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.
+The final pair's original result, feedback mode, normalized confirmed issues 
and evidence gaps must agree with each other and with the final round's 
complete historical dispositions and proposed session result.
+If session validation fails, inspect the exact failure and correct only an 
unsupported record or missing evidence; never rewrite an original assessment or 
change issue mappings merely to obtain a pass.
+Only the coordinator emits the final report under the Output Contract after 
validation succeeds.
+
 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.
 
@@ -254,7 +307,7 @@ When confirmed blockers coexist with a proven gap that 
could hide more blockers,
 ## Formal Decision Contract
 
 Map the canonical assessment to Formal Review only after the Completion Gate
-evaluation:
+evaluation and Independent Review Coordination:
 
 1. If the gate fails because a gap satisfies the Review Incomplete Proof Gate, 
use `Review Incomplete`, even when some blockers are already confirmed.
 2. If the Mandatory Style Verification Gate is incomplete for any applicable 
PR-impact file, use `Review Incomplete`, even when some blockers are already 
confirmed.
@@ -317,6 +370,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.
 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/scripts/review_ledger.py 
b/.codex/skills/review-pr/scripts/review_ledger.py
index 4d68a4069a1..e330cc859b2 100755
--- a/.codex/skills/review-pr/scripts/review_ledger.py
+++ b/.codex/skills/review-pr/scripts/review_ledger.py
@@ -742,6 +742,158 @@ def cmd_validate_incomplete(args: argparse.Namespace) -> 
int:
     return 0
 
 
+def read_review_record(reference: Any) -> dict[str, Any]:
+    if not isinstance(reference, dict) or any(not has_text(reference.get(key)) 
for key in ("path", "sha256")):
+        raise RuntimeError("Review references require a path and original 
content hash")
+    content = Path(reference["path"]).read_bytes()
+    if hashlib.sha256(content).hexdigest() != reference["sha256"]:
+        raise RuntimeError("An original review result changed after 
collection")
+    review = json.loads(content)
+    if not isinstance(review, dict) or any(not has_text(review.get(key)) for 
key in ("reviewer", "ledger", "ledger_sha256")):
+        raise RuntimeError("Review results require an isolated reviewer 
identity and original ledger reference")
+    validate_review_decision(review)
+    ledger_file = 
ensure_safe_ledger_file(resolve_ledger_file(review["ledger"]))
+    if hashlib.sha256(ledger_file.read_bytes()).hexdigest() != 
review["ledger_sha256"]:
+        raise RuntimeError("An original review ledger changed after 
collection")
+    return {**review, "path": str(Path(reference["path"]).resolve()), 
"ledger_file": str(ledger_file), "ledger": read_ledger(ledger_file)}
+
+
+def validate_review_decision(assessment: dict[str, Any]) -> None:
+    result, feedback = assessment.get("result"), 
assessment.get("feedback_mode")
+    if not isinstance(result, str) or not isinstance(feedback, str):
+        raise RuntimeError("Review result and feedback mode must be strings")
+    if result not in {"Mergeable", "Not Mergeable", "Review Incomplete"} or 
feedback not in {"", "Change Request", "Needs Discussion"}:
+        raise RuntimeError("Review decisions require a supported result and 
feedback mode")
+    if ("Not Mergeable" == result) != bool(feedback):
+        raise RuntimeError("Only Not Mergeable decisions require a feedback 
mode")
+
+
+def review_input_identity(ledger: dict[str, Any]) -> str:
+    basis = ledger["basis"]
+    return digest({"focus": basis["focus"], "baseline": 
basis["baseline"]["tree"], "delta": basis["delta"],
+                   "requirements": sorted((each["role"], each["origin"], 
each["mode"], each["sha256"]) for each in basis["sources"]
+                                          if each["role"] in {"rule", 
"requirement"})})
+
+
+def result_review_errors(proof: Any, reviews: dict[str, dict[str, Any]], 
own_reviewer: str = "") -> 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"]:
+        raise RuntimeError("Result review requires current source evidence")
+    errors = []
+    for entry in proof["evidence"]:
+        if not isinstance(entry, dict) or not has_text(entry.get("reviewer")) 
or not isinstance(entry.get("anchor"), dict):
+            raise RuntimeError("Result-review evidence requires a reviewer and 
captured source anchor")
+        reviewer = entry["reviewer"]
+        if reviewer not in reviews or own_reviewer and reviewer != 
own_reviewer:
+            errors.append("Result-review evidence does not belong to the 
reviewed original result")
+            continue
+        anchor = entry["anchor"]
+        validate_anchor_shape(anchor, captured=True)
+        if capture_anchor(anchor, reviews[reviewer]["ledger"]) != 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"]) not in {2, 4}:
+        raise RuntimeError("A review session requires one or two 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)
+    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}):
+            raise RuntimeError("Independent reviews must have distinct 
identities, results and ledgers")
+    errors = []
+    if len({review_input_identity(each["ledger"]) for each in records}) != 1:
+        errors.append("Review candidates, original baselines, requirements or 
focus differ")
+    source_versions = {}
+    for record in records:
+        ledger = record["ledger"]
+        errors.extend(validate_ledger(ledger, incomplete_result="Review 
Incomplete" == record["result"]))
+        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")
+        for source in ledger["basis"]["sources"]:
+            version = (source["mode"], source["sha256"])
+            if source["origin"] in source_versions and 
source_versions[source["origin"]] != version:
+                errors.append("Independent reviews disagree on a shared source 
version")
+            source_versions[source["origin"]] = version
+    known_reviews = {}
+    original_findings = set()
+    prior_ids = set()
+    final_groups = []
+    final_members = {}
+    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")
+        pair = records[index * 2:index * 2 + 2]
+        known_reviews.update((each["reviewer"], each) for each in pair)
+        for record in pair:
+            original_findings.update((record["reviewer"], each["id"]) for each 
in record["ledger"]["findings"])
+        audited_reviewers = []
+        for report_review in review_round["report_reviews"]:
+            if not isinstance(report_review, dict) or not 
has_text(report_review.get("reviewer")):
+                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))
+        if Counter(audited_reviewers) != Counter(each["reviewer"] for each in 
pair):
+            errors.append("Both original reports require substantive result 
review, even when they agree")
+        groups, members = {}, {}
+        for finding in review_round["findings"]:
+            if not isinstance(finding, dict) or not 
has_text(finding.get("id")) or not isinstance(finding.get("status"), str):
+                raise RuntimeError("Finding dispositions require a stable 
identity and supported status")
+            if finding["status"] not in FINDING_STATUSES:
+                raise RuntimeError("Finding dispositions require a stable 
identity and supported status")
+            if not isinstance(finding.get("members"), list):
+                raise RuntimeError("Finding dispositions require explicit 
original finding mappings")
+            if finding["id"] in groups:
+                errors.append("Canonical finding identities are duplicated")
+            groups[finding["id"]] = finding
+            errors.extend(result_review_errors(finding, known_reviews))
+            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")
+                key = (member["reviewer"], member["finding"])
+                if key in members:
+                    errors.append("An original finding has multiple 
dispositions")
+                members[key] = finding["id"]
+        if set(members) != original_findings:
+            errors.append("Every historical candidate requires exactly one 
explicit disposition")
+        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
+    if any(each["status"] == "candidate" for each in final_groups):
+        errors.append("Result-review candidates remain unresolved")
+    final_confirmed = {each["id"] for each in final_groups if each["status"] 
== "confirmed"}
+    final_gaps = {each["id"] for each in final_groups if each["status"] == 
"review-incomplete-gap"}
+    expected_result = "Review Incomplete" if final_gaps else "Not Mergeable" 
if final_confirmed else "Mergeable"
+    if session["result"] != expected_result:
+        errors.append("The final decision contradicts the complete historical 
dispositions")
+    expected = (session["result"], session["feedback_mode"], final_confirmed, 
final_gaps)
+    for record in records[-2:]:
+        finding_sets = []
+        for status in ("confirmed", "review-incomplete-gap"):
+            finding_sets.append({final_members[(record["reviewer"], 
each["id"])] for each in record["ledger"]["findings"]
+                                 if each["status"] == status and 
(record["reviewer"], each["id"]) in final_members})
+        if (record["result"], record["feedback_mode"], *finding_sets) != 
expected:
+            errors.append("The latest pair does not agree with the result 
review and all historical findings")
+    return errors
+
+
+def cmd_validate_session(args: argparse.Namespace) -> int:
+    errors = 
validate_session(json.loads(Path(args.session).read_text(encoding="utf-8")))
+    if errors:
+        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")
+    return 0
+
+
 def cmd_status(args: argparse.Namespace) -> int:
     ledger = read_ledger(args.ledger)
     file_counts = Counter(each["status"] for each in ledger["files"])
@@ -824,6 +976,9 @@ def build_parser() -> argparse.ArgumentParser:
     validate_incomplete = subparsers.add_parser("validate-incomplete", 
help="Validate a proven Review Incomplete result")
     validate_incomplete.add_argument("--ledger", required=True, help="Ledger 
file or directory")
     validate_incomplete.set_defaults(func=cmd_validate_incomplete)
+    validate_session_parser = subparsers.add_parser("validate-session", 
help="Validate two blind reviews and substantive result review")
+    validate_session_parser.add_argument("--session", required=True, 
help="JSON original result references and result-review dispositions")
+    validate_session_parser.set_defaults(func=cmd_validate_session)
     status = subparsers.add_parser("status", help="Print compact coverage 
status")
     status.add_argument("--ledger", required=True, help="Ledger file or 
directory")
     status.set_defaults(func=cmd_status)
diff --git a/.codex/skills/review-pr/tests/test_review_ledger.py 
b/.codex/skills/review-pr/tests/test_review_ledger.py
index 72594645100..09aa7d23acd 100644
--- a/.codex/skills/review-pr/tests/test_review_ledger.py
+++ b/.codex/skills/review-pr/tests/test_review_ledger.py
@@ -15,6 +15,7 @@
 # limitations under the License.
 #
 
+import hashlib
 import json
 import os
 import subprocess
@@ -641,6 +642,282 @@ class ReviewLedgerTest(unittest.TestCase):
         actual["findings"] = [finding]
         self.assertIn("G1 incomplete gap is missing real unavailability-proof 
anchors", ledger.validate_ledger(actual, incomplete_result=True))
 
+    def test_session_requires_each_report_evidence_review(self):
+        original = self.create_session([[], []])
+        self.assertEqual([], ledger.validate_session(original))
+        for missing in ("report", "reason", "counter_evidence", "evidence", 
"current_anchor", "own_evidence", "valid_range"):
+            with self.subTest(missing=missing):
+                actual = json.loads(json.dumps(original))
+                report_reviews = actual["rounds"][0]["report_reviews"]
+                if "report" == missing:
+                    report_reviews.pop()
+                elif "current_anchor" == missing:
+                    report_reviews[0]["evidence"][0]["anchor"]["range_sha256"] 
= "0" * 64
+                elif "own_evidence" == missing:
+                    report_reviews[0]["evidence"][0]["reviewer"] = "review-2"
+                elif "valid_range" == missing:
+                    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"}:
+                    self.assert_session_cli_rejected(actual)
+                else:
+                    self.assertTrue(ledger.validate_session(actual))
+
+    def test_session_normalizes_matching_root_causes(self):
+        actual = self.create_session([[("F1", "confirmed")], [("F2", 
"confirmed")]])
+        findings = actual["rounds"][0]["findings"]
+        findings[0]["members"].extend(findings.pop()["members"])
+        self.assertEqual([], ledger.validate_session(actual))
+
+    def test_session_accepts_verified_maintainer_decision(self):
+        (self.repo / "Rules.md").write_text("The candidate's project 
commitment requires an explicit maintainer decision before merge.\n", 
encoding="utf-8")
+        actual = self.create_session([[("F1", "confirmed")], [("F2", 
"confirmed")]])
+        for index, reference in enumerate(actual["reviews"]):
+            report = 
json.loads(Path(reference["path"]).read_text(encoding="utf-8"))
+            actual_ledger = ledger.read_ledger(report["ledger"])
+            rule_anchor = ledger.capture_anchor({"side": "source", "origin": 
"repository:Rules.md", "path": "Rules.md", "start_line": 1, "end_line": 1}, 
actual_ledger)
+            actual_ledger["findings"][0].update({
+                "origin": "repository:Rules.md", "fix_boundary": 
"maintainer-decision:project-commitment",
+                "reason": "The evidenced project commitment needs a maintainer 
decision.",
+                "necessity": "The project rule reserves acceptance of this 
commitment to maintainers.",
+                "evidence": ["Rules.md explicitly requires a maintainer 
decision before merge."],
+                "full_path": ["Reviewed candidate to project commitment to 
required maintainer decision."],
+                "counter_evidence": ["No existing project rule delegates this 
decision to the reviewer."],
+                "scope_proof": "The reviewed candidate is governed by the 
project commitment rule.", "anchors": [rule_anchor],
+            })
+            ledger_file = Path(report["ledger"])
+            ledger.write_ledger(ledger_file, actual_ledger)
+            report.update({"ledger_sha256": 
hashlib.sha256(ledger_file.read_bytes()).hexdigest(), "feedback_mode": "Needs 
Discussion"})
+            self.save_session_report(actual, index, report)
+        findings = actual["rounds"][0]["findings"]
+        findings[0]["members"].extend(findings.pop()["members"])
+        findings[0].update({"id": "project-commitment-decision", "reason": 
"The confirmed project rule requires a maintainer decision.",
+                            "counter_evidence": "Reviewer agreement cannot 
replace the required maintainer decision.",
+                            "evidence": [{"reviewer": "review-2", "anchor": 
rule_anchor}]})
+        actual["feedback_mode"] = "Needs Discussion"
+        self.assertEqual([], ledger.validate_session(actual))
+
+    def test_session_preserves_all_candidate_dispositions(self):
+        original = self.create_session([[("F1", "withdrawn"), ("F2", 
"non-blocking"), ("F3", "out-of-scope")], []])
+        self.assertEqual([], ledger.validate_session(original))
+        for missing in ("member", "finding", "disposition"):
+            with self.subTest(missing=missing):
+                actual = json.loads(json.dumps(original))
+                findings = actual["rounds"][0]["findings"]
+                if "member" == missing:
+                    findings[0]["members"] = []
+                elif "finding" == missing:
+                    findings.pop()
+                else:
+                    findings[0]["status"] = "candidate"
+                self.assertTrue(ledger.validate_session(actual))
+
+    def 
test_session_rejects_same_count_with_different_findings_or_feedback(self):
+        actual = self.create_session([[("F1", "confirmed")], [("F2", 
"confirmed")]])
+        self.assertTrue(ledger.validate_session(actual))
+        
actual["rounds"][0]["findings"][0]["members"].extend(actual["rounds"][0]["findings"].pop()["members"])
+        report = 
json.loads(Path(actual["reviews"][1]["path"]).read_text(encoding="utf-8"))
+        report["feedback_mode"] = "Needs Discussion"
+        self.save_session_report(actual, 1, report)
+        self.assertTrue(ledger.validate_session(actual))
+
+    def test_session_allows_second_pair_after_disagreement(self):
+        actual = self.create_session([[("F1", "confirmed")], [], [("F1", 
"confirmed")], [("F1", "confirmed")]])
+        self.assertEqual([], ledger.validate_session(actual))
+
+    def test_session_preserves_finding_discovered_during_result_review(self):
+        original = self.create_session([[], [], [("F1", "confirmed")], [("F1", 
"confirmed")]])
+        original["rounds"][0]["findings"].append({
+            "id": "normalized-F1", "members": [], "status": "confirmed",
+            "reason": "The report evidence review found an unsupported 
triggering path.",
+            "counter_evidence": "The supported alternative does not protect 
this path.",
+            "evidence": original["rounds"][0]["report_reviews"][0]["evidence"],
+        })
+        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))
+        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):
+        original = self.create_session([[("F1", "confirmed")], [("F1", 
"confirmed"), ("F2", "confirmed")],
+                                        [("F1", "confirmed")], [("F1", 
"confirmed")]])
+        self.assertTrue(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))
+        actual["rounds"][-1]["findings"][-1]["status"] = "withdrawn"
+        self.assertEqual([], ledger.validate_session(actual))
+
+    def test_session_limits_complete_review_pairs(self):
+        original = self.create_session([[("F1", "confirmed")], [], [("F1", 
"confirmed")], [("F1", "confirmed")]])
+        self.assertEqual([], ledger.validate_session(original))
+        for count in (0, 1, 3, 6):
+            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_rejects_reused_or_modified_original_records(self):
+        original = self.create_session([[], []])
+        original_report = 
Path(original["reviews"][1]["path"]).read_text(encoding="utf-8")
+        for invalid in ("report_path", "reviewer", "ledger_path", 
"report_hash", "ledger_hash"):
+            with self.subTest(invalid=invalid):
+                actual = json.loads(json.dumps(original))
+                report = json.loads(original_report)
+                if "report_path" == invalid:
+                    actual["reviews"][1] = actual["reviews"][0]
+                elif "report_hash" == invalid:
+                    actual["reviews"][1]["sha256"] = "0" * 64
+                else:
+                    if "reviewer" == invalid:
+                        report["reviewer"] = "review-1"
+                        actual["rounds"][0]["report_reviews"][1]["reviewer"] = 
"review-1"
+                        
actual["rounds"][0]["report_reviews"][1]["evidence"][0]["reviewer"] = "review-1"
+                    elif "ledger_path" == invalid:
+                        report["ledger"] = 
json.loads(Path(actual["reviews"][0]["path"]).read_text(encoding="utf-8"))["ledger"]
+                        report["ledger_sha256"] = 
hashlib.sha256(Path(report["ledger"]).read_bytes()).hexdigest()
+                    else:
+                        report["ledger_sha256"] = "0" * 64
+                    self.save_session_report(actual, 1, report)
+                self.assert_session_cli_rejected(actual)
+                
Path(original["reviews"][1]["path"]).write_text(original_report, 
encoding="utf-8")
+
+    def test_session_requires_same_current_review_basis(self):
+        actual = self.create_session([[], []])
+        report = 
json.loads(Path(actual["reviews"][1]["path"]).read_text(encoding="utf-8"))
+        original = ledger.read_ledger(report["ledger"])
+        for changed in ("focus", "rule", "requirement", "evidence_origin"):
+            with self.subTest(changed=changed):
+                actual_ledger = json.loads(json.dumps(original))
+                basis = json.loads(self.basis_file.read_text(encoding="utf-8"))
+                if "focus" == changed:
+                    basis["focus"] = "CI Review"
+                else:
+                    role = "evidence" if "evidence_origin" == changed else 
changed
+                    source = next(each for each in basis["sources"] if role == 
each["role"])
+                    alternate = self.private_root / (changed + ".txt")
+                    alternate.write_text("A different reviewed source 
contract.\n", encoding="utf-8")
+                    source["path"] = str(alternate)
+                self.update_session_basis(actual, 1, actual_ledger, basis)
+                self.assertEqual([], ledger.validate_ledger(actual_ledger))
+                self.assertTrue(ledger.validate_session(actual))
+        self.update_session_basis(actual, 1, original, 
json.loads(self.basis_file.read_text(encoding="utf-8")))
+        (self.repo / 
self.source_path).write_text("first\nchanged_after_review\nthird\nfourth\nfifth\nnew_second\nlast\n",
 encoding="utf-8")
+        self.assertTrue(ledger.validate_session(actual))
+
+    def test_session_accepts_different_discovered_evidence(self):
+        actual = self.create_session([[], []])
+        report = 
json.loads(Path(actual["reviews"][1]["path"]).read_text(encoding="utf-8"))
+        actual_ledger = ledger.read_ledger(report["ledger"])
+        basis = json.loads(self.basis_file.read_text(encoding="utf-8"))
+        evidence = self.private_root / "additional-evidence.txt"
+        evidence.write_text("Additional independent discovery evidence.\n", 
encoding="utf-8")
+        basis["sources"].append({"role": "evidence", "origin": 
"discovery:second-review", "path": str(evidence)})
+        self.update_session_basis(actual, 1, actual_ledger, basis)
+        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))
+        actual = self.create_session([[], []])
+        actual["result"] = "Review Incomplete"
+        self.assertTrue(ledger.validate_session(actual))
+
+    def 
test_session_cli_validates_without_writing_and_preserves_legacy_validation(self):
+        actual = self.create_session([[], []])
+        session_file = self.private_root / "session.json"
+        session_file.write_text(json.dumps(actual), encoding="utf-8")
+        original_files = {path: path.read_bytes() for path in 
self.private_root.rglob("*.json")}
+        for command in (["validate-session", "--session", str(session_file)],
+                        ["validate", "--ledger", 
json.loads(Path(actual["reviews"][0]["path"]).read_text(encoding="utf-8"))["ledger"]]):
+            with self.subTest(command=command[0]), patch.object(sys, "argv", 
["review_ledger.py", *command]), redirect_stdout(StringIO()):
+                self.assertEqual(0, ledger.main())
+        self.assertEqual(original_files, {path: path.read_bytes() for path in 
original_files})
+        original = json.loads(json.dumps(actual))
+        for invalid in ("unfinished", "shape", "result", "feedback_mode", 
"finding_status"):
+            with self.subTest(invalid=invalid):
+                actual = json.loads(json.dumps(original))
+                if "unfinished" == invalid:
+                    actual["rounds"][0]["report_reviews"] = []
+                elif "shape" == invalid:
+                    actual = {"reviews": "invalid"}
+                elif "finding_status" == invalid:
+                    actual["rounds"][0]["findings"] = [{"id": "invalid", 
"members": [], "status": []}]
+                else:
+                    actual[invalid] = []
+                session_file.write_text(json.dumps(actual), encoding="utf-8")
+                with patch.object(sys, "argv", ["review_ledger.py", 
"validate-session", "--session", str(session_file)]), \
+                        redirect_stdout(StringIO()), 
redirect_stderr(StringIO()):
+                    self.assertEqual(1, ledger.main())
+
+    def create_session(self, review_findings):
+        original = self.create_valid_ledger()
+        actual = {"reviews": [], "rounds": [], "result": "Mergeable", 
"feedback_mode": ""}
+        findings = {}
+        report_reviews = []
+        for index, each in enumerate(review_findings):
+            reviewer = "review-" + str(index + 1)
+            actual_ledger = json.loads(json.dumps(original))
+            for finding_id, status in each:
+                finding = self.create_incomplete_gap() if 
"review-incomplete-gap" == status else self.create_confirmed_finding(finding_id)
+                finding.update({"id": finding_id, "status": status, 
"fix_boundary": finding_id})
+                actual_ledger["findings"].append(finding)
+            ledger_file = ledger.ledger_root() / reviewer / 
ledger.LEDGER_FILE_NAME
+            ledger_file.parent.mkdir(exist_ok=True)
+            ledger.write_ledger(ledger_file, actual_ledger)
+            statuses = {finding["status"] for finding in 
actual_ledger["findings"]}
+            result = "Not Mergeable" if "confirmed" in statuses else "Review 
Incomplete" if "review-incomplete-gap" in statuses else "Mergeable"
+            report = {"reviewer": reviewer, "ledger": str(ledger_file), 
"ledger_sha256": hashlib.sha256(ledger_file.read_bytes()).hexdigest(),
+                      "result": result, "feedback_mode": "Change Request" if 
"Not Mergeable" == result else ""}
+            actual["reviews"].append({"path": str(self.private_root / 
(reviewer + ".json")), "sha256": ""})
+            self.save_session_report(actual, index, report)
+            evidence = [{"reviewer": reviewer, "anchor": 
actual_ledger["files"][0]["coverage"][0]["anchors"][0]}]
+            report_reviews.append({"reviewer": reviewer, "reason": "Checked 
the report against its owning source contract.",
+                                   "counter_evidence": "Checked the supported 
alternative behavior.", "evidence": evidence})
+            for finding in actual_ledger["findings"]:
+                disposition = findings.setdefault(finding["id"], {"id": 
"normalized-" + finding["id"], "members": [], "status": finding["status"],
+                                                                  "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:
+                actual["rounds"].append({"report_reviews": report_reviews, 
"findings": json.loads(json.dumps(list(findings.values())))})
+                report_reviews = []
+            actual.update({"result": report["result"], "feedback_mode": 
report["feedback_mode"]})
+        return actual
+
+    def save_session_report(self, session, index, report):
+        path = Path(session["reviews"][index]["path"])
+        path.write_text(json.dumps(report), encoding="utf-8")
+        session["reviews"][index]["sha256"] = 
hashlib.sha256(path.read_bytes()).hexdigest()
+
+    def assert_session_cli_rejected(self, session):
+        session_file = self.private_root / "rejected-session.json"
+        session_file.write_text(json.dumps(session), encoding="utf-8")
+        output = StringIO()
+        with patch.object(sys, "argv", ["review_ledger.py", 
"validate-session", "--session", str(session_file)]), \
+                redirect_stdout(output), redirect_stderr(StringIO()):
+            self.assertEqual(1, ledger.main())
+        self.assertNotIn("OK:", output.getvalue())
+
+    def update_session_basis(self, session, index, actual_ledger, basis):
+        basis_file = self.private_root / ("independent-basis-" + str(index) + 
".json")
+        basis_file.write_text(json.dumps(basis), encoding="utf-8")
+        actual_ledger["scope"]["review_basis_file"] = str(basis_file)
+        actual_ledger["basis"], _ = 
ledger.capture_basis(actual_ledger["scope"])
+        for review_pass in actual_ledger["passes"]:
+            review_pass["basis_fingerprint"] = 
ledger.basis_fingerprint(actual_ledger)
+        report = 
json.loads(Path(session["reviews"][index]["path"]).read_text(encoding="utf-8"))
+        ledger_file = Path(report["ledger"])
+        ledger.write_ledger(ledger_file, actual_ledger)
+        report["ledger_sha256"] = 
hashlib.sha256(ledger_file.read_bytes()).hexdigest()
+        self.save_session_report(session, index, report)
+
     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)]

Reply via email to