From 4d4f92efe8dee34de429fda9427684d505e259cf Mon Sep 17 00:00:00 2001 From: MarketDataApp <112879596+MarketDataApp@users.noreply.github.com> Date: Wed, 16 Sep 2026 18:59:53 -0400 Subject: [PATCH] feat(ci): call the code review bot, dispatch only for now Adds the caller workflow and a policy, with the automatic trigger written out and COMMENTED. Held back on purpose: the default review is language-agnostic, and whether it says useful things about sdk-java is the one thing nobody has measured. One review read by a person comes before every open pull request gets one. The trigger, when armed, is CI's COMPLETION and not the push. pull_request_target fires on opened and synchronize -- the moment CI starts -- and the bot refuses to review a commit whose checks are still running, so a push-triggered review skips and never returns while reporting success. Measured on sdk-py: tests take about 75 seconds, a review reaches that gate in 30 to 40. The named workflow is "Tests", this repository's pull-request CI, found by looking at which checks actually appear on an open pull request rather than by guessing from filenames. Codex first with Claude as the fallback, matching sdk-py. fallback rather than first because under first a live-but-broken credential ends the review instead of falling through, and codex first because the shared credential lease is declined for a backend that is reached rarely or never. --- .github/code-review/policy.yml | 38 ++++++++++++++++++ .github/workflows/code-review.yml | 66 +++++++++++++++++++++++++++++++ 2 files changed, 104 insertions(+) create mode 100644 .github/code-review/policy.yml create mode 100644 .github/workflows/code-review.yml diff --git a/.github/code-review/policy.yml b/.github/code-review/policy.yml new file mode 100644 index 0000000..da16783 --- /dev/null +++ b/.github/code-review/policy.yml @@ -0,0 +1,38 @@ +# Code review bot, sdk-java. Overrides the bot's defaults; anything absent keeps +# the default. See MarketData-App/code-review-bot/reviewbot/defaults/policy.yml + +# Codex reviews this repository; Claude catches it when Codex cannot. +# +# `fallback`, NOT `first`. Under `first` the run is [ready[0].review(brief)] +# and a BackendError goes straight to _fail -- Claude is never tried, because it +# was dropped into `missing` the moment Codex probed live. Since +# CodexBackend.probe() returns true as soon as $CODEX_HOME/auth.json EXISTS, a +# stale borrowed credential is live-but-broken, and that would end the review +# with a neutral "Bot error" and no comment. +# +# Codex must stay FIRST for a second reason: credential-checkout declines the +# shared credential lease when codex is not the first backend, because a later +# backend is reached rarely or never and holding one plan's lease for it starves +# the repositories that use it on every run. +backends: [codex, claude] +mode: fallback + +# The check is advisory until someone adds it to a branch rule, so a blocked +# review costs nothing yet and means something later. +gate: true +require_ci_green: true + +# No REVIEW.md here yet, so this repository gets the bot's language-agnostic +# default review. A sdk-java-specific REVIEW.md is worth writing once someone who +# knows this codebase has read a few reviews and can say what the model keeps +# missing. Guessing at it first would be inventing house rules nobody agreed. + +ignore_paths: + - "docs/**" + - "*.md" + - ".github/**/*.md" + - "**/*.lock" + - "**/dist/**" + +check_name: "Code review" +timeout_minutes: 15 diff --git a/.github/workflows/code-review.yml b/.github/workflows/code-review.yml new file mode 100644 index 0000000..fca1239 --- /dev/null +++ b/.github/workflows/code-review.yml @@ -0,0 +1,66 @@ +name: Code review + +# DISPATCH ONLY FOR NOW, DELIBERATELY. +# +# The automatic trigger is written out below and commented. It is held back so +# the first review on this repository is read by a person before every open +# pull request gets one: the default review is language-agnostic, and whether it +# says useful things about sdk-java is the one thing nobody has measured yet. +# +# TO ARM IT: delete the `workflow_run` comment markers below, and delete this +# banner. Nothing else changes. Read docs/setup.md section 4 in +# MarketData-App/code-review-bot first -- it explains why the trigger is CI's +# completion and not the push. +on: + # workflow_run: + # # The `name:` of this repository's pull-request CI, not its filename. + # workflows: ["Tests", "Lint"] + # types: [completed] + issue_comment: + types: [created] + workflow_dispatch: + inputs: + pr: + description: Pull request number + required: true + force: + description: > + Review a commit that has already been reviewed. The bot skips an + unchanged head, so without this a re-dispatch is a no-op that reports + success. + type: boolean + default: false + +jobs: + review: + # Guards, each of which cost a failed run elsewhere to learn: + # 1. workflow_run fires for default-branch pushes too, where there is no + # pull request. `workflow_run.event` is what triggered the UPSTREAM + # run, so 'pull_request' is the precise discriminator. + # 2. A comment only starts a review when it is addressed to the bot. + if: > + (github.event_name != 'workflow_run' || + github.event.workflow_run.event == 'pull_request') && + (github.event_name != 'issue_comment' || + (github.event.issue.pull_request != null && + startsWith(github.event.comment.body, '@marketdata-code-review'))) + uses: MarketData-App/code-review-bot/.github/workflows/review.yml@main + # NOT `secrets: inherit`. That only passes secrets to a reusable workflow in + # the SAME organisation. This repository is on the MarketDataApp user account + # and the bot is owned by MarketData-App, so inherit crosses an owner + # boundary and delivers nothing -- the call fails at evaluation naming a + # secret that is sitting in this repository's settings. + secrets: + CODE_REVIEW_APP_PRIVATE_KEY: ${{ secrets.CODE_REVIEW_APP_PRIVATE_KEY }} + CLAUDE_CODE_OAUTH_TOKEN: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }} + with: + # REQUIRED: this repository is public and on the MarketDataApp user + # account, so it can never reach the organisation's self-hosted runner -- + # that runner group is visibility=all with + # allows_public_repositories=false. A job that asks for it is not refused, + # it queues forever with no runner and nothing saying why. + runs-on: '["ubuntu-latest"]' + # `|| false` is load-bearing: `inputs` exists only for workflow_dispatch, + # and a null against a boolean-typed input fails the ENTIRE workflow at + # evaluation -- no job, no log. + force: ${{ inputs.force || false }}