Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 16 additions & 0 deletions .github/scripts/pr-review/test.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,22 @@ const workflowSource = fs.readFileSync(
),
"utf8",
);
// Deprecated caller inputs must not override the policy that is used for
// execution, session restore, readiness evidence, or review publication.
assert.doesNotMatch(workflowSource, /\$\{\{ inputs\.(?:model|effort|max-diff-bytes) \}\}/);
assert.match(workflowSource, /^ review:\n(?:(?!^ \S)[\s\S])*?^ REVIEW_MODEL: gpt-6-sol$/m);
assert.match(workflowSource, /^ review:\n(?:(?!^ \S)[\s\S])*?^ REVIEW_EFFORT: low$/m);
assert.match(workflowSource, /^ review:\n(?:(?!^ \S)[\s\S])*?^ REVIEW_MAX_DIFF_BYTES: '100000000'$/m);
assert.match(workflowSource, /^ publish:\n(?:(?!^ \S)[\s\S])*?^ REVIEW_MODEL: gpt-6-sol$/m);
assert.match(workflowSource, /^ publish:\n(?:(?!^ \S)[\s\S])*?^ REVIEW_EFFORT: low$/m);
assert.match(workflowSource, /MAX_DIFF_BYTES: \$\{\{ env\.REVIEW_MAX_DIFF_BYTES \}\}/);
assert.equal((workflowSource.match(/MODEL: \$\{\{ env\.REVIEW_MODEL \}\}/g) ?? []).length, 5);
assert.equal((workflowSource.match(/EFFORT: \$\{\{ env\.REVIEW_EFFORT \}\}/g) ?? []).length, 5);
const dispatchSource = fs.readFileSync(
path.join(path.dirname(new URL(import.meta.url).pathname), "..", "..", "workflows", "openai-pr-review-dispatch.yml"),
"utf8",
);
assert.doesNotMatch(dispatchSource, /^ (?:model|effort|max-diff-bytes):/m);
assert.match(
workflowSource,
/^ rerun-guard:\n(?:(?!^ \S)[\s\S])*?RUN_ATTEMPT: \$\{\{ github\.run_attempt \}\}(?:(?!^ \S)[\s\S])*?if \[ "\$RUN_ATTEMPT" -ne 1 \]; then(?:(?!^ \S)[\s\S])*?exit 1$/m,
Expand Down
40 changes: 23 additions & 17 deletions .github/workflows/codex-openai-review.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,24 +4,24 @@ on:
workflow_call:
inputs:
model:
description: OpenAI model used by Codex for this review.
description: Deprecated compatibility input; the shared reviewer always uses gpt-6-sol.
required: false
default: gpt-5.6-terra
default: gpt-6-sol
type: string
effort:
description: Reasoning effort supplied to Codex for this review.
description: Deprecated compatibility input; the shared reviewer always uses low reasoning effort.
required: false
default: medium
default: low
type: string
codex-version:
description: Exact Codex CLI version used so persisted sessions remain compatible.
required: false
default: 0.145.0
type: string
max-diff-bytes:
description: Maximum complete base-to-head diff size accepted for one PR.
description: Deprecated compatibility input; the shared reviewer always accepts up to 100000000 diff bytes.
required: false
default: 1000000
default: 100000000
type: number
chunk-target-bytes:
description: Deterministic target size for each first-review or incremental diff chunk.
Expand Down Expand Up @@ -317,6 +317,9 @@ jobs:
|| (steps.evaluate_readiness.outcome == 'failure' && 'Could not produce structured PR readiness evidence.')
|| '' }}
env:
REVIEW_MODEL: gpt-6-sol
REVIEW_EFFORT: low
REVIEW_MAX_DIFF_BYTES: '100000000'

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep the automatic-review diff cap within a bounded cost envelope

This reusable workflow is triggered automatically for external PRs, yet this raises the always-enforced complete-diff limit to 100 MB while the default chunk target remains 180 KB. A contributor can therefore force roughly hundreds of sequential Codex review chunks in one automatic run, creating a practical API-credit/runner-consumption denial of service for every caller. The former caller-specific cap let each repository choose its cost envelope; use a substantially bounded shared limit or retain a trusted caller-configurable cap.

Useful? React with 👍 / 👎.

PULL_REQUEST_NUMBER: ${{ needs.resolve.outputs.number }}
PR_BASE_SHA: ${{ needs.resolve.outputs.base_sha }}
PR_BASE_REF: ${{ needs.resolve.outputs.base_ref }}
Expand Down Expand Up @@ -691,8 +694,8 @@ jobs:
env:
EXPECTED_ARTIFACT_ID: ${{ steps.find_session.outputs.artifact_id }}
EXPECTED_CODEX_VERSION: ${{ inputs.codex-version }}
EXPECTED_MODEL: ${{ inputs.model }}
EXPECTED_EFFORT: ${{ inputs.effort }}
EXPECTED_MODEL: ${{ env.REVIEW_MODEL }}
EXPECTED_EFFORT: ${{ env.REVIEW_EFFORT }}
REPOSITORY_ID: ${{ github.repository_id }}
REVIEW_INSTRUCTIONS: >-
${{ inputs.review-instructions }}
Expand Down Expand Up @@ -840,7 +843,7 @@ jobs:
id: prepare_diff
env:
REPOSITORY_DIR: ${{ env.PR_DIFF_REPOSITORY }}
MAX_DIFF_BYTES: ${{ inputs.max-diff-bytes }}
MAX_DIFF_BYTES: ${{ env.REVIEW_MAX_DIFF_BYTES }}
CHUNK_TARGET_BYTES: ${{ inputs.chunk-target-bytes }}
READINESS_CONTEXT_SHA256: ${{ steps.prepare_readiness.outputs.context_sha256 }}
run: node "$REVIEWER_SOURCE_DIR/.github/scripts/pr-review/prepare.mjs"
Expand Down Expand Up @@ -879,8 +882,8 @@ jobs:
GENERATION_KEY: ${{ steps.prepare_diff.outputs.generation_key }}
GENERATION_REUSED: ${{ steps.prepare_diff.outputs.reused }}
RESUMED_SESSION_ID: ${{ steps.restore_session.outputs.session_id }}
MODEL: ${{ inputs.model }}
EFFORT: ${{ inputs.effort }}
MODEL: ${{ env.REVIEW_MODEL }}
EFFORT: ${{ env.REVIEW_EFFORT }}
REVIEW_INSTRUCTIONS: ${{ inputs.review-instructions }}
ISSUE_REVIEW_INSTRUCTIONS: ${{ inputs.issue-review-instructions }}
PR_REVIEW_INSTRUCTIONS: ${{ inputs.pr-readiness-instructions }}
Expand All @@ -893,8 +896,8 @@ jobs:
env:
REVIEW: ${{ steps.run_review.outputs.review }}
WORKFLOW_SOURCE_SHA: ${{ job.workflow_sha }}
MODEL: ${{ inputs.model }}
EFFORT: ${{ inputs.effort }}
MODEL: ${{ env.REVIEW_MODEL }}
EFFORT: ${{ env.REVIEW_EFFORT }}
run: >-
node
"$REVIEWER_SOURCE_DIR/.github/scripts/pr-readiness/evaluate.mjs"
Expand All @@ -921,8 +924,8 @@ jobs:
continue-on-error: true
env:
CODEX_VERSION: ${{ inputs.codex-version }}
MODEL: ${{ inputs.model }}
EFFORT: ${{ inputs.effort }}
MODEL: ${{ env.REVIEW_MODEL }}
EFFORT: ${{ env.REVIEW_EFFORT }}
REPOSITORY_ID: ${{ github.repository_id }}
WORKFLOW_SOURCE_SHA: ${{ job.workflow_sha }}
REVIEW_INSTRUCTIONS: >-
Expand Down Expand Up @@ -1179,6 +1182,9 @@ jobs:
contents: read
issues: read
pull-requests: write
env:
REVIEW_MODEL: gpt-6-sol
REVIEW_EFFORT: low
outputs:
failure_reason: >-
${{ (steps.verify_identity.outcome == 'failure'
Expand Down Expand Up @@ -1216,8 +1222,8 @@ jobs:
env:
REVIEW: ${{ needs.review.outputs.review }}
READINESS_EVIDENCE: ${{ needs.review.outputs.readiness_evidence }}
MODEL: ${{ inputs.model }}
EFFORT: ${{ inputs.effort }}
MODEL: ${{ env.REVIEW_MODEL }}
EFFORT: ${{ env.REVIEW_EFFORT }}
DURATION_SECONDS: ${{ needs.review.outputs.duration_seconds }}
USAGE_AVAILABLE: ${{ needs.review.outputs.usage_available }}
INPUT_TOKENS: ${{ needs.review.outputs.input_tokens }}
Expand Down
4 changes: 0 additions & 4 deletions .github/workflows/openai-pr-review-dispatch.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,8 +30,6 @@ jobs:
if: github.event_name != 'issues'
uses: ./.github/workflows/codex-openai-review.yml
with:
model: gpt-5.6-terra
effort: medium
review-instructions: >-
Review the pull-request diff and report actionable findings.
issue-review-instructions: >-
Expand Down Expand Up @@ -101,8 +99,6 @@ jobs:
pull_request_number: ${{ fromJSON(needs.resolve_linked_prs.outputs.pull_requests) }}
uses: ./.github/workflows/codex-openai-review.yml
with:
model: gpt-5.6-terra
effort: medium
review-instructions: >-
Review the pull-request diff and report actionable findings.
issue-review-instructions: >-
Expand Down
18 changes: 11 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,10 +16,15 @@ only the `uses` reference from the local path to the protected release:
uses: GizClaw/github-workflows/.github/workflows/codex-openai-review.yml@v1
```

It must pass an `OPENAI_API_KEY` Actions secret explicitly. Set `model`,
`effort`, `review-instructions`, `issue-review-instructions`, and
It must pass an `OPENAI_API_KEY` Actions secret explicitly. Set
`review-instructions`, `issue-review-instructions`, and
`pr-readiness-instructions` in the caller to match the repository's trusted
policy. The caller must grant `checks: write` so the
policy. The shared reviewer always uses `gpt-6-sol` with `low` reasoning
effort (the Codex setting corresponding to a light review) and accepts complete
diffs up to 100,000,000 bytes. The legacy `model`, `effort`, and `max-diff-bytes`
inputs remain accepted for pinned callers but are ignored. Existing callers
must update their pinned workflow reference to receive this policy; they can
then remove those three inputs. The caller must grant `checks: write` so the
shared reviewer can expose its lifecycle on the reviewed PR head, and
`pull-requests: write` for request reactions on PR comments and native review
publication. It must also grant `issues: write` for Issue-triggered refreshes
Expand Down Expand Up @@ -137,10 +142,9 @@ upload succeeds.
review requests when opening, editing, or pushing to an eligible PR. Use a
dedicated API project with appropriate usage limits and restrict the
organization secret to selected repositories.
- Complete diffs larger than 1 MB fail before Codex runs by default, limiting
untrusted input and avoiding unbounded model usage. Callers can explicitly
set `max-diff-bytes` and `chunk-target-bytes` when their cost policy permits
larger reviews.
- Complete diffs larger than 100,000,000 bytes fail before Codex runs. The
`chunk-target-bytes` input still controls deterministic chunk sizing; the
legacy `max-diff-bytes` input no longer changes the total limit.
- The reviewer checks out only the trusted base commit, reads the PR diff as
untrusted data, never checks out or executes PR-head code, and publishes
validated native inline review comments only on added lines.
Expand Down
Loading