diff --git a/.github/aicodingflow-tests/test_codex_model_provider_config.py b/.github/aicodingflow-tests/test_codex_model_provider_config.py index b692ff4..f1a9b62 100644 --- a/.github/aicodingflow-tests/test_codex_model_provider_config.py +++ b/.github/aicodingflow-tests/test_codex_model_provider_config.py @@ -18,6 +18,7 @@ ".github/workflows/update-pr-review.yml", ".github/workflows/update-triage.yml", ) +PINNED_REVIEW_ACTION = "uses: openai/codex-action@52fe01ec70a42f454c9d2ebd47598f9fd6893d56" class CodexModelProviderConfigTest(unittest.TestCase): @@ -25,7 +26,10 @@ def test_all_codex_workflows_use_shared_provider_configuration(self) -> None: for path in CODEX_WORKFLOWS: with self.subTest(path=path): contents = (ROOT / path).read_text(encoding="utf-8") - self.assertEqual(contents.count("uses: openai/codex-action@v1"), 1) + if path.endswith("/review-pr.yml"): + self.assertEqual(contents.count(PINNED_REVIEW_ACTION), 1) + else: + self.assertEqual(contents.count("uses: openai/codex-action@v1"), 1) self.assertEqual(contents.count("openai-api-key: ${{ secrets.CODEX_API_KEY }}"), 1) self.assertEqual( contents.count("model: ${{ vars.CODEX_MODEL }}"), diff --git a/.github/aicodingflow-tests/test_review_workflow_dispatch.py b/.github/aicodingflow-tests/test_review_workflow_dispatch.py index cbe03cf..28ccc03 100644 --- a/.github/aicodingflow-tests/test_review_workflow_dispatch.py +++ b/.github/aicodingflow-tests/test_review_workflow_dispatch.py @@ -149,9 +149,11 @@ def test_review_workflow_resolves_pr_before_checkout_and_uses_normalized_event(s self.assertIn("cp -R .agents/contracts pr-worktree/.agents/contracts", prepare_step["run"]) ai_step = next(step for step in review_steps if step.get("name") == "Run AI review") - self.assertEqual(ai_step["id"], "ai_review") + self.assertEqual( + ai_step["uses"], + "openai/codex-action@52fe01ec70a42f454c9d2ebd47598f9fd6893d56", + ) self.assertEqual(ai_step["timeout-minutes"], 20) - self.assertEqual(ai_step["with"]["output-file"], "pr-worktree/codex-final-message.txt") self.assertIs(ai_step["with"]["allow-bots"], True) self.assertIn("First change directory to pr-worktree", ai_step["with"]["prompt"]) self.assertIn("Read .agents/contracts/review.md", ai_step["with"]["prompt"]) @@ -161,17 +163,6 @@ def test_review_workflow_resolves_pr_before_checkout_and_uses_normalized_event(s self.assertIn("target pr-worktree/review.json explicitly", ai_step["with"]["prompt"]) self.assertIn("review_discussion_context.json", ai_step["with"]["prompt"]) self.assertIn("duplicate suppression only", ai_step["with"]["prompt"]) - self.assertIn("After writing review.json, stop immediately", ai_step["with"]["prompt"]) - - start_step = next(step for step in review_steps if step.get("name") == "Log AI review inputs") - self.assertEqual(start_step["id"], "ai_review_start") - self.assertIn("selected_skill=", start_step["run"]) - self.assertIn("pr-worktree/pr_diff.txt", start_step["run"]) - - result_step = next(step for step in review_steps if step.get("name") == "Log AI review result") - self.assertEqual(result_step["if"], "always()") - self.assertIn("AI_REVIEW_OUTCOME", result_step["env"]) - self.assertIn("duration_seconds", result_step["run"]) normalize_step = next(step for step in review_steps if step.get("name") == "Normalize review output path") self.assertIn("[ ! -f pr-worktree/review.json ] && [ -f review.json ]", normalize_step["run"]) @@ -180,9 +171,6 @@ def test_review_workflow_resolves_pr_before_checkout_and_uses_normalized_event(s validate_step = next(step for step in review_steps if step.get("name") == "Validate review output") self.assertIn("pr-worktree/pr_diff.txt pr-worktree/review.json", validate_step["run"]) - artifact_step = next(step for step in review_steps if step.get("uses") == "actions/upload-artifact@v7") - self.assertIn("pr-worktree/codex-final-message.txt", artifact_step["with"]["path"]) - complete_status_step = next(step for step in review_steps if step.get("name") == "Mark PR review status complete") self.assertIn("always()", complete_status_step["if"]) self.assertIn("github.event_name != 'pull_request'", complete_status_step["if"]) diff --git a/.github/workflows/review-pr.yml b/.github/workflows/review-pr.yml index 826e647..d961f47 100644 --- a/.github/workflows/review-pr.yml +++ b/.github/workflows/review-pr.yml @@ -189,35 +189,14 @@ jobs: esac echo "url=$endpoint" >> "$GITHUB_OUTPUT" - - name: Log AI review inputs - id: ai_review_start - run: | - started_at="$(date +%s)" - echo "started_at=$started_at" >> "$GITHUB_OUTPUT" - echo "AI review inputs prepared at $(date -u +%Y-%m-%dT%H:%M:%SZ)" - echo "selected_skill=${{ steps.review_skill.outputs.path }}" - for path in \ - pr-worktree/pr_description.txt \ - pr-worktree/pr_diff.txt \ - pr-worktree/spec_context.md \ - pr-worktree/review_discussion_context.json; do - if [ -f "$path" ]; then - printf 'input=%s bytes=%s\n' "$path" "$(wc -c < "$path")" - else - echo "input=$path absent" - fi - done - - name: Run AI review - id: ai_review - uses: openai/codex-action@v1 + uses: openai/codex-action@52fe01ec70a42f454c9d2ebd47598f9fd6893d56 # v1.11 timeout-minutes: 20 with: openai-api-key: ${{ secrets.CODEX_API_KEY }} model: ${{ vars.CODEX_MODEL }} responses-api-endpoint: ${{ steps.codex_endpoint.outputs.url }} allow-bots: true - output-file: pr-worktree/codex-final-message.txt prompt: | First change directory to pr-worktree and do all file inspection from there. Read ${{ steps.review_skill.outputs.path }} in pr-worktree and follow it exactly. @@ -237,29 +216,9 @@ jobs: a root-level review.json first. Do not run validator commands embedded in the selected skill; this workflow validates review.json after Codex exits. - After writing review.json, stop immediately and return a concise final - message. Do not re-read or revise review.json after it is complete. Do not run git commands, call GitHub APIs, post reviews, or modify files other than review.json. - - name: Log AI review result - if: always() - env: - STARTED_AT: ${{ steps.ai_review_start.outputs.started_at }} - AI_REVIEW_OUTCOME: ${{ steps.ai_review.outcome }} - run: | - finished_at="$(date +%s)" - started_at="${STARTED_AT:-$finished_at}" - duration=$((finished_at - started_at)) - echo "AI review outcome=$AI_REVIEW_OUTCOME duration_seconds=$duration" - for path in pr-worktree/review.json pr-worktree/codex-final-message.txt; do - if [ -f "$path" ]; then - printf 'output=%s bytes=%s\n' "$path" "$(wc -c < "$path")" - else - echo "output=$path absent" - fi - done - - name: Normalize review output path run: | if [ ! -f pr-worktree/review.json ] && [ -f review.json ]; then @@ -286,7 +245,6 @@ jobs: pr-worktree/spec_context.md pr-worktree/review_discussion_context.json pr-worktree/review.json - pr-worktree/codex-final-message.txt - name: Mark PR review status complete if: always() && github.event_name != 'pull_request' && steps.pr.outputs.head_sha != '' && steps.pr.outputs.head_repo == github.repository