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 5b3ac0c3f37 Enforce scoped verification and local review workflow
(#39704)
5b3ac0c3f37 is described below
commit 5b3ac0c3f37bf8cb337e6062b2e5ea33b837b0c1
Author: Liang Zhang <[email protected]>
AuthorDate: Thu Sep 3 12:33:36 2026 +0800
Enforce scoped verification and local review workflow (#39704)
---
.codex/context/change-completion.md | 16 ++--
.codex/harness/agents/cases.toml | 94 ++++++++++++++++++++++
.codex/harness/agents/policy-sources.toml | 4 +-
.../code-implementation/references/verification.md | 9 ++-
.codex/skills/review-pr/SKILL.md | 20 ++---
.../review-pr/scripts/build_review_inventory.py | 10 ++-
.codex/skills/review-pr/scripts/review_common.py | 26 +++++-
.codex/skills/review-pr/scripts/review_ledger.py | 10 ++-
.../review-pr/tests/test_build_review_inventory.py | 36 ++++++++-
.../skills/review-pr/tests/test_review_ledger.py | 16 ++++
AGENTS.md | 6 +-
11 files changed, 213 insertions(+), 34 deletions(-)
diff --git a/.codex/context/change-completion.md
b/.codex/context/change-completion.md
index 33d7941cacb..cb68187af39 100644
--- a/.codex/context/change-completion.md
+++ b/.codex/context/change-completion.md
@@ -22,14 +22,14 @@ For an authorized change, build, implement, or fix request,
excluding a standalo
1. Perform the post-write task-delta audit required by the Strict Scope and
Task-Delta Gate in `AGENTS.md`, using the read-only `git diff`, surrounding
context, and acceptance checklist as evidence.
2. Confirm every task-introduced file and hunk is necessary, expected changed
files contain only their frozen change intent, prohibited paths have zero task
delta, direct reuse was considered, architecture changes were authorized, and
every test protects owned behavior. Inspect Java changes for newly added
local-variable `final`, scan changed code and Skills for local absolute paths,
and remove only current-task violations.
3. Treat scripts, searches, formatting, compilation, passing tests, and
benchmarks as evidence, not proof of semantic compliance. Judge the final
behavior, contracts, architecture, user request, and affected performance paths
directly; fix every safe in-scope violation instead of reporting it as an
accepted risk.
-4. Apply the `$code-simplification` trigger and limits defined in
`.codex/context/cross-cutting-skills.md`.
-5. Review the effective local candidate against the same code-correctness
gates used by `$review-pr`: root cause and fix mapping, affected behavior, side
effects and regressions, contracts and architecture, test validity, and
adversarial cases. Apply `$code-review-and-quality` when available, or its
equivalent review, for the general quality axes; it does not replace these
shared correctness gates.
-6. When the implementation targets an existing PR, apply the `$review-pr`
pre-handoff workflow defined in `AGENTS.md`. PR-specific public-head and
remote-evidence checks remain part of formal PR review.
-7. Verify the functional and performance non-regression gate in
`.codex/skills/code-implementation/references/rules/non-regression.md` after
the last relevant write. Treat any invalidated or inconclusive required
evidence as incomplete work, not residual risk.
-8. Fix every safe in-scope required finding, rerun invalidated checks, and
repeat the applicable reviews. If a finding requires scope expansion, an
unresolved architecture choice, or a high-risk action, stop at its existing
authorization gate instead of fixing it automatically.
-9. Hand off and propose a commit message only after one complete applicable
review pass finds zero new required issues. Before that review passes, do not
hand off or propose a commit message. Do not defer a locally discoverable
required finding to a later formal PR review.
-10. Stop when no required in-scope finding remains. Do not iterate for
optional polish, broad cleanup, or risky refactoring.
+4. Apply the `$code-simplification` trigger and limits defined in
`.codex/context/cross-cutting-skills.md`. Apply `$code-review-and-quality` when
available, or its equivalent review, for the general quality axes; it does not
replace the required `$review-pr` Formal Review.
+5. Run the applicable verification in
`.codex/skills/code-implementation/references/verification.md`, including the
final file-filtered Spotless and Checkstyle commands, then verify the
functional and performance non-regression gate in
`.codex/skills/code-implementation/references/rules/non-regression.md`. Treat
any invalidated, unmatched, or inconclusive required evidence as incomplete
work, not residual risk.
+6. Invoke `$review-pr` in Formal Review Mode on the effective local candidate
for every task covered by this loop. For a standalone local task, review the
original task baseline plus only the task-owned delta; for a task targeting an
existing PR, also apply the PR-specific public-head and remote-evidence checks.
+7. If Formal Review returns `Not Mergeable` with safe in-scope required
findings, fix them, return to the task-delta audit, rerun every invalidated
check, and invoke `$review-pr` again. Do not defer a locally discoverable
required finding to another review.
+8. If Formal Review returns `Review Incomplete`, `Not Mergeable` with
`Feedback Mode: Needs Discussion`, or a finding requires scope expansion, an
unresolved architecture choice, or a high-risk action, stop at the existing
evidence, decision, or authorization gate and keep the task incomplete.
+9. Hand off only after the latest complete Formal Review returns `Review
Result: Mergeable`, and include that final formal report as its own fenced
`markdown` block. Do not present an earlier or non-passing review as the final
report.
+10. After the fenced `markdown` formal report, provide the proposed commit
message in a fenced `text` block and the Git commands in a fenced shell block
labeled `bash` or `sh`, both outside the review report. Generate `git commit
--dry-run --only -m <message> -- <exact-task-paths>` and `git commit --only -m
<message> -- <exact-task-paths>` for only task-owned files that contain no
unrelated hunks; list any task-created untracked file in an exact `git add --
<new-task-paths>` command firs [...]
If a report or verdict is disproved, fix the highest-leverage rule, schema,
validator, prompt, or regression case before correcting the artifact, unless
the user explicitly requests a one-off correction.
-The final response must lead with the outcome and include changed files and
rationale, commands with exit codes, verification status, remaining risks, and
the next action only when one is still required. When Git commit authorization
is absent, provide a proposed commit message without staging or committing.
+The final response must lead with the outcome and include changed files and
rationale, commands with exit codes, verification status, the final Formal
Review report, remaining risks, and the next action only when one is still
required. When Git commit authorization is absent, provide the separate
proposed commit message and exact task-file commands without staging or
committing.
diff --git a/.codex/harness/agents/cases.toml b/.codex/harness/agents/cases.toml
index 57ec3de48be..6de22581160 100644
--- a/.codex/harness/agents/cases.toml
+++ b/.codex/harness/agents/cases.toml
@@ -1663,3 +1663,97 @@ required_reasons = ["code_policy_required",
"output_capture_required"]
policy_binding_profile = "test-write"
policy_assertions = [{source = "code-implementation", statements = ["For every
code-affecting write, read
`.codex/skills/code-implementation/references/rules/implementation.md`,
`.codex/skills/code-implementation/references/rules/artifact-removal-and-contract-impact.md`,
and `.codex/skills/code-implementation/references/rules/non-regression.md`.",
"Apply each contract, impact, or removal rule only when its own trigger
matches."]}, {source = "contracts-and-removal", statements = ["Regener [...]
critical = true
+
+[[cases]]
+id = "final_style_checks_target_task_files"
+group = "coding / regular"
+description = "Runs final Spotless and Checkstyle verification only for the
task-changed files."
+phase = "verification and ordinary completion loop"
+ordinary_loop = true
+profile = "code-write"
+prompt = "The final code write changed only
`module-a/src/main/java/example/Foo.java` and
`module-a/src/test/java/example/FooTest.java`; unrelated working-tree changes
exist elsewhere. Run the mandatory final Spotless and Checkstyle verification
before Formal Review, using the smallest safe scope, and do not edit, review,
hand off, or change Git."
+decision = "proceed"
+required_actions = ["run_local_checks", "wrap_high_output"]
+allowed_actions = ["inspect_local", "run_local_checks", "wrap_high_output"]
+forbidden_actions = ["edit_code", "edit_non_code", "run_pre_handoff_review",
"propose_commit_message", "mutate_git", "mutate_remote"]
+required_reasons = ["output_capture_required"]
+required_summary_terms = ["Spotless", "Checkstyle", "only"]
+policy_binding_profile = "code-write"
+policy_assertions = [{source = "code-verification", statements = ["After the
last code-affecting action and before the pre-handoff Formal Review, run both
Spotless and Checkstyle for every applicable task-changed code file; run
Spotless for a documentation-only change when the configured formatter governs
that file.", "Treat a filtered command as passing only after its output and the
post-command task-delta inspection prove that every applicable task file was
selected, no non-allowlisted [...]
+critical = true
+
+[[cases]]
+id = "standalone_local_completion_invokes_review_pr"
+group = "coding / regular"
+description = "Requires an actual review-pr Formal Review for a standalone
local task before handoff."
+phase = "ordinary completion loop"
+ordinary_loop = true
+profile = "code-write"
+prompt = "A standalone authorized local implementation has no public PR. Its
task delta, final file-scoped checks, and non-regression evidence are complete,
but `$review-pr` has not run. Complete the task now with commit artifacts."
+decision = "proceed"
+required_actions = ["run_pre_handoff_review"]
+allowed_actions = ["inspect_local", "run_pre_handoff_review"]
+forbidden_actions = ["edit_code", "edit_non_code", "propose_commit_message",
"mutate_git", "mutate_remote"]
+required_reasons = ["pre_handoff_review_required"]
+required_summary_terms = ["Formal Review", "Mergeable"]
+policy_binding_profile = "code-write"
+policy_assertions = [{source = "agents", statements = ["Every authorized
change, build, implement, or fix request requires a pre-handoff `$review-pr`
Formal Review of the effective local candidate, except a governed standalone
restoration or rollback.", "Hand off or propose commit artifacts only after the
latest complete Formal Review returns `Review Result: Mergeable`."]}, {source =
"change-completion", statements = ["Invoke `$review-pr` in Formal Review Mode
on the effective local cand [...]
+critical = true
+
+[[cases]]
+id = "not_mergeable_reenters_completion_loop"
+group = "coding / regular"
+description = "Fixes safe review-pr findings and repeats invalidated checks
and Formal Review until Mergeable."
+phase = "ordinary completion loop"
+ordinary_loop = true
+profile = "code-write"
+prompt = "`$review-pr` Formal Review returned `Not Mergeable` with one
confirmed safe in-scope Change Request. Fix it and complete the task; the fix
invalidates the final Spotless, Checkstyle, and review evidence, and those
Maven checks may output more than 100 lines. Do not hand off or propose commit
artifacts before a new passing review."
+decision = "proceed"
+required_actions = ["edit_code", "fix_review_findings", "run_local_checks",
"wrap_high_output", "run_pre_handoff_review"]
+allowed_actions = ["inspect_local", "edit_code", "fix_review_findings",
"run_local_checks", "wrap_high_output", "run_pre_handoff_review"]
+forbidden_actions = ["edit_non_code", "propose_commit_message", "mutate_git",
"mutate_remote"]
+required_reasons = ["safe_in_scope_review_finding",
"pre_handoff_review_required", "output_capture_required"]
+required_summary_terms = ["Spotless", "Checkstyle"]
+required_summary_term_groups = [["Formal Review", "review"], ["Mergeable",
"passing"]]
+policy_binding_profile = "code-write"
+policy_assertions = [{source = "change-completion", statements = ["If Formal
Review returns `Not Mergeable` with safe in-scope required findings, fix them,
return to the task-delta audit, rerun every invalidated check, and invoke
`$review-pr` again.", "Hand off only after the latest complete Formal Review
returns `Review Result: Mergeable`, and include that final formal report as its
own fenced `markdown` block."]}, {source = "code-verification", statements =
["Capture high-volume output [...]
+critical = true
+
+[[cases]]
+id = "final_review_and_git_artifacts_are_separate"
+group = "operations"
+description = "Outputs task-file commit artifacts after the passing Formal
Review was returned separately."
+phase = "completion output gate"
+ordinary_loop = true
+profile = "code-write"
+prompt = "The latest `$review-pr` Formal Review of a standalone local task
returned `Review Result: Mergeable`, and its complete formal report was already
returned as a separate artifact. The only task-owned tracked files are
`module-a/src/main/java/example/Foo.java` and
`module-a/src/test/java/example/FooTest.java`, neither contains unrelated
hunks, and no Git write is authorized. Return only the proposed commit message
and exact manual commit commands now."
+decision = "proceed"
+required_actions = ["propose_commit_message"]
+allowed_actions = ["inspect_local", "propose_commit_message"]
+forbidden_actions = ["edit_code", "edit_non_code", "mutate_git",
"mutate_remote"]
+required_reasons = ["review_passed", "git_read_only"]
+required_summary_terms = ["git commit --dry-run --only", "git commit --only",
"Foo.java", "FooTest.java"]
+forbidden_summary_terms = ["### Result", "### Evidence", "### Coverage"]
+policy_binding_profile = "code-write"
+policy_assertions = [{source = "change-completion", statements = ["After the
fenced `markdown` formal report, provide the proposed commit message in a
fenced `text` block and the Git commands in a fenced shell block labeled `bash`
or `sh`, both outside the review report.", "Generate `git commit --dry-run
--only -m <message> -- <exact-task-paths>` and `git commit --only -m <message>
-- <exact-task-paths>` for only task-owned files that contain no unrelated
hunks; list any task-created unt [...]
+critical = true
+
+[[cases]]
+id = "standalone_local_review_pr_artifact"
+group = "operations"
+description = "Reviews a standalone local candidate without requiring a public
PR and keeps Git artifacts outside the review."
+phase = "review scope and output gate"
+ordinary_loop = true
+profile = "review-pr"
+prompt = "The repository completion loop invoked Formal Review for a
standalone local candidate with an established task baseline and attributed
local delta but no public PR. The complete Code Correctness Review found no
blocker. Return the review artifact now; the outer workflow, not this reviewer,
will provide any commit message and Git commands."
+decision = "proceed"
+required_actions = []
+allowed_actions = []
+forbidden_actions = ["edit_code", "edit_non_code", "propose_commit_message",
"mutate_git", "mutate_remote"]
+required_reasons = []
+required_summary_terms = ["### Result", "Review Result: Mergeable", "###
Evidence", "### Coverage"]
+required_summary_term_groups = [["standalone", "no public PR"], ["baseline",
"established task-local delta", "attributed local candidate"]]
+forbidden_summary_terms = ["git commit"]
+policy_binding_profile = "review-pr"
+policy_assertions = [{source = "agents", statements = ["Complete reviews or
recommendations use Formal Review with `### Result`, one `Review Result`, `###
Evidence`, and `### Coverage`; Coverage identifies local-only changes."]},
{source = "review-pr", statements = ["For a standalone local candidate, use the
active task's original working-tree baseline plus only its attributed local
commits, index changes, and working-tree changes as the effective candidate.",
"Use the frozen task bounda [...]
+critical = true
diff --git a/.codex/harness/agents/policy-sources.toml
b/.codex/harness/agents/policy-sources.toml
index cb4364bf25d..624ff268e1b 100644
--- a/.codex/harness/agents/policy-sources.toml
+++ b/.codex/harness/agents/policy-sources.toml
@@ -70,7 +70,7 @@ id = "change-completion"
path = ".codex/context/change-completion.md"
kind = "context"
semantic = true
-references = ["AGENTS.md", ".codex/context/cross-cutting-skills.md",
".codex/skills/code-implementation/references/rules/non-regression.md",
".codex/skills/review-pr/SKILL.md"]
+references = ["AGENTS.md", ".codex/context/cross-cutting-skills.md",
".codex/skills/code-implementation/references/rules/non-regression.md",
".codex/skills/code-implementation/references/verification.md",
".codex/skills/review-pr/SKILL.md"]
[[sources]]
id = "policy-maintenance"
@@ -196,7 +196,7 @@ id = "review-pr"
path = ".codex/skills/review-pr/SKILL.md"
kind = "skill"
semantic = true
-references = ["AGENTS.md", "CODE_OF_CONDUCT.md",
".codex/skills/review-pr/references/evidence-access.md",
".codex/skills/review-pr/references/high-risk-review.md",
".codex/skills/review-pr/references/sql-parser-review.md",
".codex/skills/review-pr/references/review-corrections.md",
".codex/skills/code-implementation/references/rules/implementation.md",
".codex/skills/code-implementation/references/rules/testing.md",
".codex/skills/code-implementation/references/rules/artifact-removal-and [...]
+references = ["AGENTS.md", "CODE_OF_CONDUCT.md",
".codex/context/change-completion.md",
".codex/skills/review-pr/references/evidence-access.md",
".codex/skills/review-pr/references/high-risk-review.md",
".codex/skills/review-pr/references/sql-parser-review.md",
".codex/skills/review-pr/references/review-corrections.md",
".codex/skills/code-implementation/references/rules/implementation.md",
".codex/skills/code-implementation/references/rules/testing.md",
".codex/skills/code-implementatio [...]
[[sources]]
id = "review-pr-evidence"
diff --git a/.codex/skills/code-implementation/references/verification.md
b/.codex/skills/code-implementation/references/verification.md
index 4d51e4d3dd8..a8574d15971 100644
--- a/.codex/skills/code-implementation/references/verification.md
+++ b/.codex/skills/code-implementation/references/verification.md
@@ -31,10 +31,11 @@ Keep background unit tests under 60 seconds. Capture
high-volume output accordin
For every user-forbidden tool, API, assertion, or pattern, run a scoped final
search and report the command and result. Do not rely only on plan compliance.
-After the last file-changing action:
+After the last code-affecting action and before the pre-handoff Formal Review,
run both Spotless and Checkstyle for every applicable task-changed code file;
run Spotless for a documentation-only change when the configured formatter
governs that file.
-1. Run `./mvnw spotless:apply -Pcheck -T1C` for code or documentation changes.
-2. Run `./mvnw checkstyle:check -Pcheck -T1C` when production, test, or
project-rule files changed.
-3. Do not manually reformat afterward. Any later edit invalidates formatting
and requires the applicable checks again.
+1. Resolve the exact task-changed files governed by Spotless and their owning
Maven projects, then run `./mvnw -pl <explicit-owner>
-DspotlessFiles='<comma-separated exact absolute-path regular expressions>'
spotless:apply -Pcheck -T1C` for each smallest owner set. Construct each
regular expression from the current workspace and repository-relative task path
without hard-coding a local workspace path, and ensure it can match only an
allowlisted task file before running this file-modifying goal.
+2. For production or test Java changes, run `./mvnw -pl <explicit-owner>
-Dcheckstyle.includes='<comma-separated source-root-relative task files>'
-Dcheckstyle.includeResources=false -Dcheckstyle.includeTestResources=false
checkstyle:check -Pcheck -T1C` once per owning Maven project. For a changed
resource or project-rule file governed by Checkstyle, use its exact supported
resource filter; if the configured plugin cannot isolate that file, stop and
report the minimum unavoidable scope i [...]
+3. Treat a filtered command as passing only after its output and the
post-command task-delta inspection prove that every applicable task file was
selected, no non-allowlisted file was written, and the command exited
successfully. A successful command that matched no intended file is not
evidence.
+4. Do not manually reformat afterward. Any later edit invalidates the affected
Spotless and Checkstyle evidence and requires both applicable checks to run
again before another Formal Review.
Record every required command, exit code, decisive result, and remaining
unverified path. A required check with missing, incomparable, unstable, or
inconclusive evidence is not a pass.
diff --git a/.codex/skills/review-pr/SKILL.md b/.codex/skills/review-pr/SKILL.md
index a69f47464b0..bc67635fe07 100644
--- a/.codex/skills/review-pr/SKILL.md
+++ b/.codex/skills/review-pr/SKILL.md
@@ -236,6 +236,7 @@ readiness conclusion.
omission-prone review. It mechanically accounts for files, clusters, risk
axes, finding classifications, proof fields, and review passes; it does not
judge semantic correctness.
+- For a standalone local candidate with index or working-tree changes, provide
the same exact task-path file to both scripts with `--candidate-files <path>`.
The scripts resolve those paths from the computed merge-base through the
working tree, include matching untracked files, and fail when a listed path is
unchanged.
- Mutate one ledger sequentially; do not run ledger commands concurrently.
- Keep temporary ledger data private and remove only the exact ledger created
for the current review.
@@ -263,15 +264,12 @@ unclear.
## Local Candidate Scope
-- Use the latest public PR head plus authorized local commits, index changes,
- and working-tree changes as the effective candidate.
-- Verify with read-only Git that local `HEAD` equals or descends from the
public head.
-- If that relationship cannot be established, record the unavailable
effective-candidate lineage as an incomplete gap; if the refs prove divergence,
resolve the correct review basis before selecting a verdict.
-- Review from the public PR merge-base through the working tree. Scope is the
- union of GitHub files and the authorized local delta; exclude unrelated local
- changes.
+- First classify the local candidate as standalone or as targeting an existing
public PR.
+- For a standalone local candidate, use the active task's original
working-tree baseline plus only its attributed local commits, index changes,
and working-tree changes as the effective candidate. Use the frozen task
boundary and task-delta audit as authoritative scope, exclude unrelated local
changes, and do not require a public head, GitHub metadata, or public lineage.
+- For a local candidate targeting an existing PR, use the latest public PR
head plus authorized local commits, index changes, and working-tree changes as
the effective candidate. Verify with read-only Git that local `HEAD` equals or
descends from the public head; if that relationship cannot be established,
record the unavailable lineage as an incomplete gap, and if the refs prove
divergence, resolve the correct review basis before selecting a verdict.
+- Review a PR-backed local candidate from the public PR merge-base through the
working tree, use the union of GitHub files and the authorized local delta, and
exclude unrelated local changes.
- Apply Code Correctness Review through the canonical assessment, including
the same proof and completion gates, triggered high-risk criteria, convergence
loop, and Formal Decision Contract.
-- State in `### Coverage` that the effective candidate includes authorized
local changes, identify their scope separately from the public PR, and never
present them as the public PR state.
+- In `### Coverage`, identify whether the candidate is standalone or PR-backed
and record its baseline and attributed local delta. For a PR-backed candidate,
identify authorized local changes separately from the public PR and never
present them as the public PR state.
- Keep this Skill review-only. The outer active implementation loop fixes safe
in-scope findings and reruns Formal Review; scope expansion, architecture
choices, and high-risk actions return to their existing authorization gates.
This Skill never activates that loop.
## Multi-Round and Challenged Findings
@@ -288,9 +286,7 @@ latest commits, exposed by the previous fix, or missed in
the previous review.
## Output Contract
-Every result returned by this Skill must be exactly one fenced `markdown` block
-with no prose before or after it. The first non-empty line must be
-```` ```markdown ```` and the last non-empty line must be ```` ``` ````.
+Every standalone result returned by this Skill must be exactly one fenced
`markdown` block with no prose before or after it. When the repository
completion loop invokes Formal Review, this fenced block remains the complete
review artifact, but the outer workflow may append only the separately fenced
non-review handoff artifacts required by `.codex/context/change-completion.md`
outside it. The first non-empty line of the review artifact must be ````
```markdown ```` and its last non-empty [...]
Use the user's language for formal results. Draft GitHub-facing discussion
replies in English unless the user requests another language. Use stable
labels,
@@ -322,7 +318,7 @@ For each blocking issue include:
- `Required Change` for Change Request, or `Discussion Needed` for Needs
Discussion.
-Do not add patch-level changes after selecting Needs Discussion. Do not
include placeholder headings. In `### Coverage`, report the candidate type,
reviewed head, authoritative requirements and files accounted for, behavior
clusters, completed discovery lenses, unresolved gaps, and CI scope. For a
local 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 o [...]
+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,
behavior clusters, completed discovery lenses, unresolved gaps, and CI scope.
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 chang [...]
### PR Discussion Reply
diff --git a/.codex/skills/review-pr/scripts/build_review_inventory.py
b/.codex/skills/review-pr/scripts/build_review_inventory.py
index 63669278bbc..b9b3582ca71 100755
--- a/.codex/skills/review-pr/scripts/build_review_inventory.py
+++ b/.codex/skills/review-pr/scripts/build_review_inventory.py
@@ -26,7 +26,9 @@ import sys
from collections import defaultdict
from typing import Iterable
-from review_common import ChangedFile, categorize, compare_github_files,
final_paths, get_repo_root, parse_name_status, run_git
+from review_common import (
+ ChangedFile, categorize, compare_github_files, final_paths, get_repo_root,
parse_name_status, resolve_candidate_changes, run_git,
+)
MAX_ITEMS = 30
@@ -57,7 +59,9 @@ def build_inventory(args: argparse.Namespace) -> dict[str,
object]:
base_sha = run_git(["rev-parse", args.base_ref], repo_root).strip()
head_sha = run_git(["rev-parse", args.head_ref], repo_root).strip()
merge_base = run_git(["merge-base", args.base_ref, args.head_ref],
repo_root).strip()
- changed_files = parse_name_status(run_git(["diff", "--name-status",
f"{merge_base}..{args.head_ref}"], repo_root))
+ candidate_files = getattr(args, "candidate_files", None)
+ changed_files = resolve_candidate_changes(repo_root, merge_base,
candidate_files) if candidate_files else parse_name_status(
+ run_git(["diff", "--name-status", f"{merge_base}..{args.head_ref}"],
repo_root))
dirty_worktree = run_git(["status", "--short"], repo_root,
allow_empty=True).splitlines()
dirty_sample, dirty_truncated = limited(dirty_worktree)
previous_delta: list[ChangedFile] = []
@@ -73,6 +77,7 @@ def build_inventory(args: argparse.Namespace) -> dict[str,
object]:
"head_sha": head_sha,
"merge_base": merge_base,
"changed_file_count": len(changed_files),
+ "candidate_files": {"provided": bool(candidate_files)},
"github_files": compare_github_files(final_paths(changed_files),
args.github_files, MAX_ITEMS),
},
"dirty_worktree": {
@@ -155,6 +160,7 @@ def parse_args() -> argparse.Namespace:
parser.add_argument("--head-ref", required=True, help="PR head ref or SHA")
parser.add_argument("--previous-head", help="Previous reviewed PR head ref
or SHA")
parser.add_argument("--github-files", help="File containing GitHub changed
paths, one per line")
+ parser.add_argument("--candidate-files", help="Exact repository-relative
local candidate paths, one per line")
parser.add_argument("--format", choices=("markdown", "json"),
default="markdown", help="Output format")
return parser.parse_args()
diff --git a/.codex/skills/review-pr/scripts/review_common.py
b/.codex/skills/review-pr/scripts/review_common.py
index 8159afd1ef5..9a013ff461a 100644
--- a/.codex/skills/review-pr/scripts/review_common.py
+++ b/.codex/skills/review-pr/scripts/review_common.py
@@ -22,7 +22,7 @@ from __future__ import annotations
import subprocess
from dataclasses import dataclass
-from pathlib import Path
+from pathlib import Path, PurePosixPath
from typing import Any, Iterable
@@ -63,6 +63,30 @@ def parse_name_status(output: str) -> list[ChangedFile]:
return result
+def resolve_candidate_changes(repo_root: Path, baseline_ref: str,
candidate_files_path: str) -> list[ChangedFile]:
+ candidate_paths = [line.strip() for line in
Path(candidate_files_path).read_text(encoding="utf-8").splitlines() if
line.strip()]
+ if not candidate_paths:
+ raise RuntimeError("Candidate file list is empty")
+ if len(candidate_paths) != len(set(candidate_paths)):
+ raise RuntimeError("Candidate file list contains duplicate paths")
+ invalid_paths = [each for each in candidate_paths
+ if PurePosixPath(each).is_absolute() or "." == each or
".." in PurePosixPath(each).parts]
+ if invalid_paths:
+ raise RuntimeError(f"Candidate file list contains invalid
repository-relative paths: {', '.join(invalid_paths)}")
+ literal_paths = [f":(literal){each}" for each in candidate_paths]
+ tracked_changes = parse_name_status(run_git(["diff", "--name-status",
baseline_ref, "--", *literal_paths], repo_root))
+ untracked_paths = run_git(["ls-files", "--others", "--exclude-standard",
"--", *literal_paths],
+ repo_root, allow_empty=True).splitlines()
+ changed_files = [*tracked_changes, *(ChangedFile(status="A", path=each)
for each in untracked_paths if each)]
+ changed_by_path = {each.path: each for each in changed_files}
+ if len(changed_by_path) != len(changed_files):
+ raise RuntimeError("Candidate scope resolves the same final path more
than once")
+ unchanged_paths = [each for each in candidate_paths if each not in
changed_by_path]
+ if unchanged_paths:
+ raise RuntimeError(f"Candidate file list contains paths that are not
changed from the baseline: {', '.join(unchanged_paths)}")
+ return [changed_by_path[each] for each in candidate_paths]
+
+
def categorize(path: str) -> str:
if "RELEASE-NOTES.md" == path:
return "release-notes"
diff --git a/.codex/skills/review-pr/scripts/review_ledger.py
b/.codex/skills/review-pr/scripts/review_ledger.py
index 43ad024f691..6361cfa49be 100755
--- a/.codex/skills/review-pr/scripts/review_ledger.py
+++ b/.codex/skills/review-pr/scripts/review_ledger.py
@@ -32,7 +32,9 @@ from collections import Counter
from pathlib import Path
from typing import Any, Iterable
-from review_common import categorize, compare_github_files, final_paths,
get_repo_root, parse_name_status, run_git
+from review_common import (
+ categorize, compare_github_files, final_paths, get_repo_root,
parse_name_status, resolve_candidate_changes, run_git,
+)
LEDGER_KIND = "review-pr-coverage-ledger"
@@ -173,7 +175,9 @@ def cmd_init(args: argparse.Namespace) -> int:
base_sha = run_git(["rev-parse", args.base_ref], repo_root).strip()
head_sha = run_git(["rev-parse", args.head_ref], repo_root).strip()
merge_base = run_git(["merge-base", args.base_ref, args.head_ref],
repo_root).strip()
- changed_files = parse_name_status(run_git(["diff", "--name-status",
f"{merge_base}..{args.head_ref}"], repo_root))
+ candidate_files = getattr(args, "candidate_files", None)
+ changed_files = resolve_candidate_changes(repo_root, merge_base,
candidate_files) if candidate_files else parse_name_status(
+ run_git(["diff", "--name-status", f"{merge_base}..{args.head_ref}"],
repo_root))
ledger_dir = create_ledger_dir(repo_root, args.pr, head_sha)
ledger_file = ledger_dir / LEDGER_FILE_NAME
now = int(time.time())
@@ -192,6 +196,7 @@ def cmd_init(args: argparse.Namespace) -> int:
"head_sha": head_sha,
"merge_base": merge_base,
"changed_file_count": len(changed_files),
+ "candidate_files": {"provided": bool(candidate_files)},
"github_files": compare_github_files(final_paths(changed_files),
args.github_files),
},
"files": [{
@@ -447,6 +452,7 @@ def build_parser() -> argparse.ArgumentParser:
init.add_argument("--base-ref", required=True, help="Base ref used to
compute merge-base")
init.add_argument("--head-ref", required=True, help="PR head ref")
init.add_argument("--github-files", help="File containing GitHub changed
paths")
+ init.add_argument("--candidate-files", help="Exact repository-relative
local candidate paths, one per line")
init.set_defaults(func=cmd_init)
mark_file = subparsers.add_parser("mark-file", help="Set one authoritative
file's coverage state")
mark_file.add_argument("--ledger", required=True, help="Ledger file or
directory")
diff --git a/.codex/skills/review-pr/tests/test_build_review_inventory.py
b/.codex/skills/review-pr/tests/test_build_review_inventory.py
index 347a6ebfa12..7786eb3fc9c 100644
--- a/.codex/skills/review-pr/tests/test_build_review_inventory.py
+++ b/.codex/skills/review-pr/tests/test_build_review_inventory.py
@@ -16,6 +16,7 @@
#
import sys
+import tempfile
import unittest
from argparse import Namespace
from pathlib import Path
@@ -27,6 +28,7 @@ if str(SCRIPTS_DIR) not in sys.path:
sys.path.insert(0, str(SCRIPTS_DIR))
import build_review_inventory as inventory
+import review_common as common
class BuildReviewInventoryTest(unittest.TestCase):
@@ -41,7 +43,7 @@ class BuildReviewInventoryTest(unittest.TestCase):
"M\tsrc/main/java/Foo.java\nA\tdocs/guide.md\n",
" M unrelated.java\n",
]
- args = Namespace(base_ref="base", head_ref="head", previous_head=None,
github_files=None)
+ args = Namespace(base_ref="base", head_ref="head", previous_head=None,
github_files=None, candidate_files=None)
actual = inventory.build_inventory(args)
self.assertEqual("merge-base", actual["scope"]["merge_base"])
self.assertEqual(2, actual["scope"]["changed_file_count"])
@@ -49,6 +51,38 @@ class BuildReviewInventoryTest(unittest.TestCase):
self.assertEqual("src/main/java/Foo.java",
actual["changed_files_by_category"]["production-java"][0]["path"])
run_git.assert_any_call(["diff", "--name-status", "merge-base..head"],
Path("/repo"))
+ @patch.object(inventory, "resolve_candidate_changes")
+ @patch.object(inventory, "run_git")
+ @patch.object(inventory, "get_repo_root", return_value=Path("/repo"))
+ def test_build_inventory_uses_exact_candidate_files(self, _, run_git,
resolve_candidate_changes):
+ run_git.side_effect = ["base-sha\n", "head-sha\n", "merge-base\n", " M
unrelated.java\n"]
+ resolve_candidate_changes.return_value =
[common.ChangedFile(status="M", path="AGENTS.md")]
+ args = Namespace(base_ref="base", head_ref="head", previous_head=None,
github_files=None, candidate_files="candidate.txt")
+ actual = inventory.build_inventory(args)
+ self.assertEqual(1, actual["scope"]["changed_file_count"])
+ self.assertEqual({"provided": True},
actual["scope"]["candidate_files"])
+ self.assertEqual("AGENTS.md",
actual["changed_files_by_category"]["docs"][0]["path"])
+ resolve_candidate_changes.assert_called_once_with(Path("/repo"),
"merge-base", "candidate.txt")
+
+ def test_resolve_candidate_changes_includes_untracked_files(self):
+ with tempfile.TemporaryDirectory() as temp_dir:
+ candidate_file = Path(temp_dir) / "candidate.txt"
+ candidate_file.write_text("AGENTS.md\n.codex/new.toml\n",
encoding="utf-8")
+ with patch.object(common, "run_git",
side_effect=["M\tAGENTS.md\n", ".codex/new.toml\n"]) as run_git:
+ actual = common.resolve_candidate_changes(Path("/repo"),
"baseline", str(candidate_file))
+ self.assertEqual([common.ChangedFile(status="M", path="AGENTS.md"),
+ common.ChangedFile(status="A",
path=".codex/new.toml")], actual)
+ literal_paths = [":(literal)AGENTS.md", ":(literal).codex/new.toml"]
+ run_git.assert_any_call(["diff", "--name-status", "baseline", "--",
*literal_paths], Path("/repo"))
+ run_git.assert_any_call(["ls-files", "--others", "--exclude-standard",
"--", *literal_paths], Path("/repo"), allow_empty=True)
+
+ def test_resolve_candidate_changes_rejects_unchanged_paths(self):
+ with tempfile.TemporaryDirectory() as temp_dir:
+ candidate_file = Path(temp_dir) / "candidate.txt"
+ candidate_file.write_text("AGENTS.md\n", encoding="utf-8")
+ with patch.object(common, "run_git", side_effect=["", ""]),
self.assertRaisesRegex(RuntimeError, "not changed"):
+ common.resolve_candidate_changes(Path("/repo"), "baseline",
str(candidate_file))
+
if __name__ == "__main__":
unittest.main()
diff --git a/.codex/skills/review-pr/tests/test_review_ledger.py
b/.codex/skills/review-pr/tests/test_review_ledger.py
index 210089862c9..8e22d1b7d96 100644
--- a/.codex/skills/review-pr/tests/test_review_ledger.py
+++ b/.codex/skills/review-pr/tests/test_review_ledger.py
@@ -29,6 +29,7 @@ SCRIPTS_DIR = Path(__file__).resolve().parents[1] / "scripts"
if str(SCRIPTS_DIR) not in sys.path:
sys.path.insert(0, str(SCRIPTS_DIR))
+import review_common as common
import review_ledger as ledger
@@ -89,6 +90,21 @@ class ReviewLedgerTest(unittest.TestCase):
errors = ledger.validate_ledger(actual_ledger)
self.assertIn("GitHub file list does not match local triple-dot
scope", errors)
+ def test_init_uses_exact_candidate_files(self):
+ changed_files = [common.ChangedFile(status="M", path="AGENTS.md")]
+ args = Namespace(repo_root=".", pr="local-task", base_ref="base",
head_ref="head", github_files=None,
+ candidate_files="candidate.txt")
+ with patch.object(ledger, "get_repo_root",
return_value=Path("/repo")), \
+ patch.object(ledger, "run_git", side_effect=["base-sha\n",
"head-sha\n", "merge-base\n"]), \
+ patch.object(ledger, "resolve_candidate_changes",
return_value=changed_files) as resolve_candidate_changes, \
+ patch.object(ledger, "create_ledger_dir",
return_value=Path("ledger")), \
+ patch.object(ledger, "write_ledger") as write_ledger,
redirect_stdout(StringIO()):
+ self.assertEqual(0, ledger.cmd_init(args))
+ actual = write_ledger.call_args.args[1]
+ self.assertEqual({"provided": True},
actual["scope"]["candidate_files"])
+ self.assertEqual("AGENTS.md", actual["files"][0]["path"])
+ resolve_candidate_changes.assert_called_once_with(Path("/repo"),
"merge-base", "candidate.txt")
+
def test_confirmed_findings_must_have_distinct_fix_boundaries(self):
actual_ledger = self.create_valid_ledger()
actual_ledger["findings"] = [self.create_confirmed_finding("F1"),
self.create_confirmed_finding("F2")]
diff --git a/AGENTS.md b/AGENTS.md
index 0487613cae2..461c5335962 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -172,9 +172,11 @@ Optional cross-cutting Skills remain governed by
`.codex/context/cross-cutting-s
## Change Completion Gate
-A pre-handoff review is required for every authorized change, build,
implement, or fix request, excluding a standalone restoration or rollback
governed by its existing gates. After the last write, audit the task delta,
complete the applicable verification, and review the effective local candidate
against the same code-correctness gates used by `$review-pr`: root-cause and
fix mapping, affected behavior, side effects and regressions, contracts and
architecture, test validity, and adversar [...]
+Every authorized change, build, implement, or fix request requires a
pre-handoff `$review-pr` Formal Review of the effective local candidate, except
a governed standalone restoration or rollback. After the last write, audit the
task delta and complete verification. Review root-cause and fix mapping,
affected behavior, side effects and regressions, contracts and architecture,
test validity, and adversarial cases.
-Fix every safe in-scope required finding, rerun every invalidated check, and
repeat the applicable review. If a finding requires scope expansion, an
unresolved architecture choice, or a high-risk action, stop at its existing
authorization gate instead of fixing it automatically. Hand off or propose a
commit message only after one complete applicable review pass finds zero new
required issues.
+Fix every safe in-scope required finding, rerun invalidated checks, and invoke
`$review-pr` again. Stop at the applicable gate for scope expansion, unresolved
architecture, or a high-risk action. Hand off or propose commit artifacts only
after the latest complete Formal Review returns `Review Result: Mergeable`.
+
+After Mergeable, output the formal report, commit message, and exact task-file
`git commit --dry-run --only` and `git commit --only` commands in individual
fenced blocks.
## Functional and Performance Non-Regression Gate