diff --git a/CHANGELOG.md b/CHANGELOG.md index f12a93c..0fd1cbb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +### Changed + +- Docs reframed for dual GitLab + GitHub support: platform auto-detection, GitHub environment/flags, both output transports, and the composite-action vs reusable-workflow `secrets: inherit` caveat ([#124]). + ## [0.8.2] - 2026-07-15 ### Added @@ -37,6 +41,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 - **BREAKING**: renamed the product-scoped environment-variable prefix `GITLAB_REVIEW_* → CODE_REVIEW_*` (e.g. `GITLAB_REVIEW_MODEL → CODE_REVIEW_MODEL`, and the namespacing shim that de-prefixes provider/infra vars in shared CI) with no backward compatibility — the old names are no longer read, so existing CI configs must rename their variables. Unprefixed GitLab tokens (`GITLAB_TOKEN`, `CI_JOB_TOKEN`, …) are unchanged ([#121]). [Unreleased]: https://github.com/weareikko/code-review/compare/0.8.2...HEAD +[#124]: https://github.com/weareikko/code-review/pull/124 [0.8.2]: https://github.com/weareikko/code-review/compare/0.8.1...0.8.2 [#123]: https://github.com/weareikko/code-review/pull/123 [0.8.1]: https://github.com/weareikko/code-review/compare/0.8.0...0.8.1 diff --git a/README.md b/README.md index ca6328a..c77b2b8 100644 --- a/README.md +++ b/README.md @@ -5,15 +5,19 @@ [![Size](https://img.shields.io/bundlephobia/minzip/@weareikko/code-review?style=flat&colorB=3e63dd&colorA=414853&label=size)](https://bundlephobia.com/package/@weareikko/code-review) ![Codecov](https://img.shields.io/codecov/c/github/weareikko/code-review?style=flat&colorB=3e63dd&colorA=414853) -Run an agent-driven code review in GitLab CI, parse inline comments, post deduplicated merge request discussions, and report per-run token usage and cost. +Run an agent-driven code review on **GitLab merge requests and GitHub pull requests** — the same review engine on both. It parses inline comments, posts deduplicated review discussions, upserts a summary, and reports per-run token usage and cost. The platform is auto-detected from the environment (GitLab CI vs. GitHub Actions) and can be forced with `--platform github|gitlab`. -The reviewer reads the MR **title and description** as the author's declared intent: it checks the diff against the stated purpose and flags code/intent mismatches (the change does something the description never claimed, or omits something it promised) as a first-class finding. A missing or empty description degrades gracefully — the review still runs. +The reviewer reads the merge/pull request **title and description** as the author's declared intent: it reads the diff against the stated purpose and surfaces code/intent mismatches (the change does something the description never claimed, or omits something it promised) as a summary note. A missing or empty description degrades gracefully — the review still runs. ## Requirements - Node.js `>=24` -- `git` available in the runtime -- A pipeline running in a merge request context (`CI_PROJECT_ID`, `CI_MERGE_REQUEST_IID`) +- `git` available in the runtime (full history — the review diffs against the merge base) +- A run in one of the two supported contexts, which the tool auto-detects: + - **GitLab CI** in a merge-request pipeline (`GITLAB_CI`, `CI_PROJECT_ID`, `CI_MERGE_REQUEST_IID`), or + - **GitHub Actions** on a `pull_request` event (`GITHUB_ACTIONS`, `GITHUB_REPOSITORY`, the PR number) + +Detection precedence: an explicit `--platform github|gitlab` (or `CODE_REVIEW_PLATFORM`) always wins; otherwise `GITHUB_ACTIONS=true` selects GitHub and `GITLAB_CI=true` (or a present `CI_PROJECT_ID` / `CI_SERVER_URL`) selects GitLab; failing that, the tool infers the platform from whichever side's identifiers are present. ## Install / Run @@ -79,13 +83,17 @@ review: - review-usage.json ``` -## GitHub Actions +## GitHub Actions example + +On GitHub the same engine reviews **pull requests** — auto-detected from the Actions environment (`GITHUB_ACTIONS`, `GITHUB_REPOSITORY`, the `pull_request` event); pass `--platform github` to force it. Findings post as one batched PR review with inline comments plus an upserted summary comment. -The same reviewer also reviews **GitHub pull requests** — the engine is platform-agnostic and auto-detects GitHub from the Actions environment (`GITHUB_ACTIONS`, `GITHUB_REPOSITORY`, the `pull_request` event). Set `--platform github` to force it. +### Composite action (recommended) -### Reusable workflow (simplest) +The bundled composite action is the primary path: it checks out the repository, sets up Node, installs the CLI, and runs the review in one step. It needs: -The quickest way to enable reviews in a repo is the bundled reusable workflow. Add a tiny caller and let it check out the code, install the CLI, and run the review for you: +- **Permissions:** `pull-requests: write` (to post the review and summary) and `contents: read` (to check out the code). The default `GITHUB_TOKEN` is enough; the action reads it as `${{ github.token }}` by default. +- **Full git history:** the action checks out the repository by default with `fetch-depth: 0` so the merge-base diff and commit log resolve (tune with `fetch-depth`, or set `checkout: false` and check out yourself first). +- **A model + its key:** pass the model via the `model` input and the provider's key via the `api-key` input (or expose the provider's standard env var, e.g. `ANTHROPIC_API_KEY`, to the step). ```yml name: code-review @@ -98,19 +106,25 @@ permissions: jobs: review: - uses: weareikko/code-review/.github/workflows/code-review.yml@0.8.2 # pin to a release tag - secrets: inherit + runs-on: ubuntu-latest + steps: + # Checkout (full history) is bundled — no separate checkout step needed. + # Opt out with `checkout: false` if your job already checked out the code. + - uses: weareikko/code-review@0.8.2 # pin to a release tag + with: + model: anthropic/claude-sonnet-4-5 + api-key: ${{ secrets.ANTHROPIC_API_KEY }} + # github-token defaults to ${{ github.token }} + # args: --min-severity warn --dry-run ``` -It reads review settings from repo/org **variables** (`CODE_REVIEW_MODEL`, `CODE_REVIEW_DEPTH`, `CODE_REVIEW_THINKING_LEVEL`, `CODE_REVIEW_VERIFY_MODEL`) and provider credentials from **secrets** named with the `CODE_REVIEW_` prefix (e.g. `CODE_REVIEW_ANTHROPIC_API_KEY`), which the CLI's env shim de-prefixes for the provider. `secrets: inherit` is required so the reusable workflow can see them. Optional `with:` inputs: `model` (overrides the `CODE_REVIEW_MODEL` variable), `version`, `node-version`, `working-directory`, `args`, and `runs-on`. +Inputs: `model` (required), `api-key`, `github-token` (default `${{ github.token }}`), `version` (npm dist-tag/version, default `latest`), `node-version` (default `24`), `working-directory`, `args` (extra CLI flags forwarded verbatim), `checkout` (default `true` — bundled repository checkout), and `fetch-depth` (default `0` — full history, required for the merge-base diff). -### Composite action +Because the composite action references your secrets directly (`${{ secrets.ANTHROPIC_API_KEY }}`), it works from **any** repository, including consumers in a different organization from this one. -For more control, use the bundled composite action directly. It needs: +### Reusable workflow (same org/enterprise) -- **Permissions:** `pull-requests: write` (to post the review and summary) and `contents: read` (to check out the code). The default `GITHUB_TOKEN` is enough; the action reads it as `${{ github.token }}` by default. -- **Full git history:** the bundled checkout uses `fetch-depth: 0` so the merge-base diff and commit log resolve (tune with `fetch-depth`, or set `checkout: false` and check out yourself). -- **A model + its key:** pass the model via the `model` input and the provider's key via the `api-key` input (or expose the provider's standard env var, e.g. `ANTHROPIC_API_KEY`, to the step). +The bundled reusable workflow lets a caller enable reviews with no `steps:` of its own — it checks out the code, installs the CLI, and runs the review for you: ```yml name: code-review @@ -123,24 +137,23 @@ permissions: jobs: review: - runs-on: ubuntu-latest - steps: - # Checkout (full history) is bundled — opt out with `checkout: false` if - # your job already checked out the code with its own options. - - uses: weareikko/code-review@main # pin to a release tag in production - with: - model: anthropic/claude-sonnet-4-5 - api-key: ${{ secrets.ANTHROPIC_API_KEY }} - # github-token defaults to ${{ github.token }} - # args: --min-severity warn --dry-run + uses: weareikko/code-review/.github/workflows/code-review.yml@0.8.2 # pin to a release tag + secrets: inherit ``` -Inputs: `model` (required), `api-key`, `github-token` (default `${{ github.token }}`), `version` (npm dist-tag/version, default `latest`), `node-version` (default `24`), `working-directory`, `args` (extra CLI flags forwarded verbatim), `checkout` (default `true` — bundled repository checkout), and `fetch-depth` (default `0` — full history, required for the merge-base diff). +It reads review settings from repo/org **variables** (`CODE_REVIEW_MODEL`, `CODE_REVIEW_DEPTH`, `CODE_REVIEW_THINKING_LEVEL`, `CODE_REVIEW_VERIFY_MODEL`) and provider credentials from **secrets** named with the `CODE_REVIEW_` prefix (e.g. `CODE_REVIEW_ANTHROPIC_API_KEY`), which the CLI's env shim de-prefixes for the provider. Optional `with:` inputs: `model` (overrides the `CODE_REVIEW_MODEL` variable), `version`, `node-version`, `working-directory`, `args`, and `runs-on`. -Prefer to run the CLI directly (no composite action)? `GITHUB_TOKEN`, `GITHUB_REPOSITORY`, and the PR number are read straight from the Actions environment: +> **Caveat — `secrets: inherit` is same-organization (or enterprise) only.** Organization secrets are **not** inherited across organizations, so this pattern only works when the caller repository lives in the same org (or enterprise) as `weareikko/code-review`. Cross-organization consumers must use the **composite action** above, which references their own secrets directly. (The reusable workflow relies on `secrets: inherit` and declares no `workflow_call.secrets`, so there is no explicit-secrets path for it.) + +### Running the CLI directly + +Prefer no action at all? `GITHUB_TOKEN`, `GITHUB_REPOSITORY`, and the PR number are read straight from the Actions environment. Check out the code first (full history) so the merge-base diff resolves: ```yml -- uses: actions/setup-node@v4 +- uses: actions/checkout@v5 + with: + fetch-depth: 0 +- uses: actions/setup-node@v5 with: node-version: 24 - run: npx @weareikko/code-review @@ -159,17 +172,17 @@ The README covers getting started. Reference material lives in [`docs/`](https:/ - [Skills](https://github.com/weareikko/code-review/blob/main/docs/skills.md) — built-in, external (`npm:`/`file:`/`git:`), and project auto-discovered review skills. - [Multi-stage review](https://github.com/weareikko/code-review/blob/main/docs/multi-stage-review.md) — the staged Find / Verify / Synthesize pipeline behind `--review-depth`. - [Observability](https://github.com/weareikko/code-review/blob/main/docs/observability.md) — diagnostics-channel tracing and the opt-in OpenTelemetry bridge (spans, metrics, logs). -- [Output format](https://github.com/weareikko/code-review/blob/main/docs/output-format.md) — inline-comment shape, MR-level summary note, footer, and duplicate prevention. +- [Output format](https://github.com/weareikko/code-review/blob/main/docs/output-format.md) — inline-comment shape, the upserted summary (a note on GitLab, an issue comment on GitHub), footer, and duplicate prevention. ## Configuration -The CLI auto-resolves most values from GitLab CI variables and provider-standard env vars. The two things you must provide are a model and its provider's API key: +The CLI auto-resolves most values from the CI environment (GitLab CI variables or the GitHub Actions context) and provider-standard env vars. The two things you must provide are a model and its provider's API key: ```bash code-review --model anthropic/claude-sonnet-4-5 --api-key "$ANTHROPIC_API_KEY" ``` -Equivalently, set `CODE_REVIEW_MODEL` and the provider's key (e.g. `ANTHROPIC_API_KEY`) as CI/CD variables. Common knobs include `--min-severity`, `--thinking`, `--posting-mode draft`, `--no-summary`, and `--dry-run`. See the full [environment-variable and flag reference](https://github.com/weareikko/code-review/blob/main/docs/configuration.md). +Equivalently, set `CODE_REVIEW_MODEL` and the provider's key (e.g. `ANTHROPIC_API_KEY`) as CI/CD variables (GitLab) or repository/organization variables and secrets (GitHub). Common knobs include `--min-severity`, `--thinking`, `--posting-mode draft`, `--no-summary`, and `--dry-run`. See the full [environment-variable and flag reference](https://github.com/weareikko/code-review/blob/main/docs/configuration.md). ## Providers @@ -182,7 +195,7 @@ Equivalently, set `CODE_REVIEW_MODEL` and the provider's key (e.g. `ANTHROPIC_AP - parsed comment payload - computed fingerprints - duplicate status - - final GitLab discussion payload + - final platform-specific posting payload (a GitLab discussion payload, or a GitHub review-comment payload) - `review-usage.json`: token and cost breakdown for the run (`tokens.{input,output,cacheRead,cacheWrite,total}`, `cost.{input,output,cacheRead,cacheWrite,total}`, `model`) The CLI also prints a one-line summary at the end of the run: @@ -198,15 +211,17 @@ Use these files for CI debugging and auditing. - **`Node.js >=24 is required`** - Use `node:24` (or newer) in CI. - **`Missing required configuration`** - - Provide required flags or ensure CI vars are available (`CI_PROJECT_ID`, `CI_MERGE_REQUEST_IID`, token, API key). + - Provide required flags or ensure the platform's identifiers/token are available: on GitLab `CI_PROJECT_ID`, `CI_MERGE_REQUEST_IID`, and a GitLab token; on GitHub `GITHUB_REPOSITORY`, the PR number, and `GITHUB_TOKEN`. A model and its API key are required on both. +- **`Could not detect the review platform` / `Ambiguous review platform`** + - Set `--platform github|gitlab` (or `CODE_REVIEW_PLATFORM`) to force the platform. - **`--min-severity must be one of: info, warn, critical`** - Fix `--min-severity` or `CODE_REVIEW_MIN_SEVERITY`. - **Git history errors / merge-base failures** - - Set `GIT_DEPTH: 0`. + - Fetch full history: `GIT_DEPTH: 0` on GitLab, `fetch-depth: 0` on `actions/checkout` (the composite action does this by default). - Ensure source and target branches are fetchable from `origin`. -- **GitLab API 401/403 when posting** - - Ensure token has rights to read MR metadata/discussions and create MR discussions. - - If using `CI_JOB_TOKEN`, ensure your GitLab project settings allow required API access. +- **API 401/403 when posting** + - GitLab: ensure the token can read MR metadata/discussions and create MR discussions; with `CI_JOB_TOKEN`, check that project settings allow the required API access. + - GitHub: ensure the token has `pull-requests: write` (the default `GITHUB_TOKEN` with that permission is enough). - **No comments posted** - Check `review-comments.json` for `duplicate: true` or empty parsed comments. - Run with `--dry-run` and inspect `code-review.md` formatting (`== Inline Comments ==`). diff --git a/docs/configuration.md b/docs/configuration.md index f218495..d3fb4fa 100644 --- a/docs/configuration.md +++ b/docs/configuration.md @@ -2,6 +2,16 @@ ← Back to the [README](../README.md) +## Platform detection + +`code-review` reviews GitLab merge requests and GitHub pull requests with the same engine, and picks the platform automatically: + +1. An explicit `--platform github|gitlab` (or `CODE_REVIEW_PLATFORM`) always wins — an unknown value is an error. +2. Otherwise CI markers decide: `GITHUB_ACTIONS=true` → GitHub; `GITLAB_CI=true` (or a present `CI_PROJECT_ID` / `CI_SERVER_URL`) → GitLab. +3. Failing that, the tool infers from whichever side's required identifiers are present (`GITHUB_REPOSITORY` + a PR number vs. `CI_PROJECT_ID` + `CI_MERGE_REQUEST_IID`). It errors when both sides are present (ambiguous) or neither is (undetectable) — set `--platform` to resolve it. + +The GitLab and GitHub identifier/token variables below are read only for the resolved platform; the model, its API key, and the shared knobs apply to both. + ## Environment variables The CLI auto-resolves values from CI variables and common token/key names. @@ -16,6 +26,11 @@ The CLI auto-resolves values from CI variables and common token/key names. | `GLAB_CLI_TOKEN` | Fallback GitLab API token (`PRIVATE-TOKEN`) | | `CI_JOB_TOKEN` | Fallback token (`JOB-TOKEN`) | | `GITLAB_PRIVATE_TOKEN` | Fallback token (`PRIVATE-TOKEN`) | +| `GITHUB_TOKEN` | GitHub API token used to read the PR and post the review/summary (default for `--github-token`). Needs `pull-requests: write`; in GitHub Actions the automatic `${{ github.token }}` with that permission works. | +| `GITHUB_REPOSITORY` | `owner/repo` slug of the GitHub repository (default for `--github-repository`). Set automatically in GitHub Actions. | +| `GITHUB_EVENT_PATH` / `GITHUB_REF` | Sources the CLI reads to resolve the pull-request number when `--pr` is absent: the event payload (`.pull_request.number`, else top-level `.number`), then a `refs/pull//merge` (or `/head`) ref. Set automatically in GitHub Actions. | +| `GITHUB_API_URL` | GitHub REST API base (default for `--github-api-url`; defaults to `https://api.github.com`). GitHub Enterprise sets it to e.g. `https://ghe.example.com/api/v3`; set automatically in GitHub Actions. | +| `CODE_REVIEW_PLATFORM` | Force the review platform (`github` or `gitlab`; default for `--platform`). Empty = auto-detect (see [Platform detection](#platform-detection)). | | `ANTHROPIC_API_KEY` / `ANTHROPIC_OAUTH_TOKEN` | AI API key for Anthropic / Claude models (oauth token takes precedence) | | `OPENAI_API_KEY` | AI API key for OpenAI models | | `GEMINI_API_KEY` | AI API key for Google Gemini models | @@ -43,7 +58,7 @@ The CLI auto-resolves values from CI variables and common token/key names. ### Namespacing provider/infra variables with `CODE_REVIEW_` -In a shared GitLab CI environment, the provider credentials and infra variables read by the AI SDK use generic, provider-standard names (`ANTHROPIC_API_KEY`, `CLOUDFLARE_API_KEY`, `CLOUDFLARE_ACCOUNT_ID`, `CLOUDFLARE_GATEWAY_ID`, `OLLAMA_HOST`, ambient AWS/Vertex creds, …). To avoid collisions with unrelated jobs and make it obvious which variables belong to code-review, you can optionally prefix any of them with `CODE_REVIEW_`. At startup, each `CODE_REVIEW_` variable is exposed as ``: +In a shared CI environment (GitLab CI, or a GitHub Actions org/repo secret set), the provider credentials and infra variables read by the AI SDK use generic, provider-standard names (`ANTHROPIC_API_KEY`, `CLOUDFLARE_API_KEY`, `CLOUDFLARE_ACCOUNT_ID`, `CLOUDFLARE_GATEWAY_ID`, `OLLAMA_HOST`, ambient AWS/Vertex creds, …). To avoid collisions with unrelated jobs and make it obvious which variables belong to code-review, you can optionally prefix any of them with `CODE_REVIEW_`. At startup, each `CODE_REVIEW_` variable is exposed as ``: ``` CODE_REVIEW_ANTHROPIC_API_KEY → ANTHROPIC_API_KEY @@ -64,6 +79,11 @@ This is purely additive — unprefixed variables keep working unchanged. When bo | `--mr ` | Merge request IID | `CI_MERGE_REQUEST_IID` | | `--gitlab-url ` | GitLab URL | `CI_SERVER_URL` or `https://${CI_SERVER_HOST}` | | `--gitlab-token ` | GitLab token | `GITLAB_TOKEN`, `GLAB_CLI_TOKEN`, `CI_JOB_TOKEN`, `GITLAB_PRIVATE_TOKEN` | +| `--platform ` | Force the review platform (`github` or `gitlab`); empty auto-detects (see [Platform detection](#platform-detection)) | `CODE_REVIEW_PLATFORM` or auto-detect | +| `--github-repository ` | GitHub repository `owner/repo` (GitHub only) | `GITHUB_REPOSITORY` | +| `--pr ` | GitHub pull-request number (GitHub only); otherwise resolved from the event payload or `GITHUB_REF` | `GITHUB_EVENT_PATH` payload / `GITHUB_REF` | +| `--github-token ` | GitHub API token (GitHub only); needs `pull-requests: write` | `GITHUB_TOKEN` | +| `--github-api-url ` | GitHub REST API base (GitHub only; e.g. `https://ghe.example.com/api/v3` on Enterprise) | `GITHUB_API_URL` or `https://api.github.com` | | `--api-key ` | AI API key. Required, except for providers with ambient credentials or local endpoints (e.g. Ollama). | The model provider's standard env var (e.g. `ANTHROPIC_API_KEY`, `OPENAI_API_KEY`, `GEMINI_API_KEY`, `OPENROUTER_API_KEY`) | | `--model ` | Model to use in `provider/modelId` format. Required — there is no default. Multi-slash IDs (e.g. `openrouter/anthropic/claude-3-opus`) are supported. Use `ollama/` for local Ollama models. | `CODE_REVIEW_MODEL` | | `--model-pool ` | Comma-separated `provider/modelId` list for heterogeneous `full`-depth review (see [Heterogeneous review with a model pool](./providers.md#heterogeneous-review-with-a-model-pool)). Empty = single model = no change. | `CODE_REVIEW_MODEL_POOL` or none | @@ -76,8 +96,8 @@ This is purely additive — unprefixed variables keep working unchanged. When bo | `--retrieve-skipped` | Stage diffs for files dropped by the size budget on disk so the reviewer can read them on demand instead of losing them. | `CODE_REVIEW_RETRIEVE_SKIPPED` or `false` | | `--min-severity ` | `info`, `warn`, `critical` | `CODE_REVIEW_MIN_SEVERITY` or `info` | | `--thinking ` | `off`, `minimal`, `low`, `medium`, `high`, `xhigh` | `CODE_REVIEW_THINKING_LEVEL` or `off` | -| `--posting-mode ` | `direct` or `draft` (atomic bulk publish) | `CODE_REVIEW_POSTING_MODE` or `direct` | -| `--no-summary` | Skip posting/updating the MR-level summary note | summary posting is on by default | +| `--posting-mode ` | `direct` or `draft` (atomic bulk publish; GitLab only — see below) | `CODE_REVIEW_POSTING_MODE` or `direct` | +| `--no-summary` | Skip posting/updating the summary (an MR note on GitLab, an issue comment on GitHub) | summary posting is on by default | | `--force-review` | Review even if the current commit was already reviewed | `CODE_REVIEW_FORCE_REVIEW` or `false` | | `--review-file ` | Raw `code-review` output file | `code-review.md` | | `--output ` | Generated payload artifact file | `review-comments.json` | @@ -90,4 +110,4 @@ This is purely additive — unprefixed variables keep working unchanged. When bo `--thinking` controls extended thinking on the underlying agent. Thinking tokens are billed at the model's output token rate, so higher levels cost more — the `Review usage:` line and `review-usage.json` reflect that cost. -`--posting-mode draft` creates GitLab draft notes for every fresh comment and publishes them atomically via `POST /draft_notes/bulk_publish`. The reviewer either appears fully on the MR or not at all, instead of leaking partial state if the job is interrupted. If a draft creation fails mid-flight, the run sweeps the partial drafts before reporting the failure; if the job is killed before that, the next run's orphan cleanup picks them up. Requires a GitLab version that exposes the `draft_notes` and `bulk_publish` endpoints (≥ 15.10) and a token whose user can own draft notes — keep `direct` for older self-hosted instances or restricted tokens. `bulk_publish` publishes _all_ of the current user's drafts on the MR, so use a dedicated bot account if multiple processes may share the token. +`--posting-mode draft` is **GitLab-only** (GitHub has no draft-then-publish flow, so the flag has no effect there). It creates GitLab draft notes for every fresh comment and publishes them atomically via `POST /draft_notes/bulk_publish`. The reviewer either appears fully on the MR or not at all, instead of leaking partial state if the job is interrupted. If a draft creation fails mid-flight, the run sweeps the partial drafts before reporting the failure; if the job is killed before that, the next run's orphan cleanup picks them up. Requires a GitLab version that exposes the `draft_notes` and `bulk_publish` endpoints (≥ 15.10) and a token whose user can own draft notes — keep `direct` for older self-hosted instances or restricted tokens. `bulk_publish` publishes _all_ of the current user's drafts on the MR, so use a dedicated bot account if multiple processes may share the token. diff --git a/docs/multi-stage-review.md b/docs/multi-stage-review.md index ea7cfdf..d5a2b17 100644 --- a/docs/multi-stage-review.md +++ b/docs/multi-stage-review.md @@ -45,7 +45,7 @@ developer can audit what the pipeline suppressed — consistent with the existin ### Why three, not five The Claude Code display also shows **Triage** and **Sweep / Verify-sweep**. Those -are refinements with a poor cost-per-yield ratio for a CI-on-every-MR reviewer: +are refinements with a poor cost-per-yield ratio for a CI-on-every-MR/PR reviewer: - **Triage** (semantic dedup _between_ finders) only earns its keep once Find fans out into multiple overlapping agents. With one finder there is nothing to @@ -97,7 +97,7 @@ This is a property of the **Find** stage and is deferred. The intended policy wh it lands: - **Coverage** (chunk-when-over-budget vs drop-when-over-budget) engages at the - default depth, conditional on size — a normal MR stays under budget and pays + default depth, conditional on size — a normal MR/PR stays under budget and pays nothing extra. - **Intensity** (multi-angle per chunk, Triage, Sweep) is what `full` buys. - `single` keeps drop-and-flag as the cheap, predictable fallback. diff --git a/docs/observability.md b/docs/observability.md index 3265b8f..495a7e1 100644 --- a/docs/observability.md +++ b/docs/observability.md @@ -2,6 +2,8 @@ ← Back to the [README](../README.md) +> **Naming note.** Telemetry is emitted on both platforms (GitLab MRs and GitHub PRs), but the diagnostics/OTel identifiers below keep their historical `gitlab_*` / `gitlab.*` prefixes for backward compatibility (renaming them would break existing dashboards and queries). Source-control API phase/channel names are the platform-neutral `scm.*` (e.g. `scm.get_merge_request`). The GitLab-CI project attributes (`gitlab.project_path`, `gitlab.project_namespace`, `gitlab.mr_target_branch`, `gitlab.pipeline_source`, `gitlab.ci_job_id`, `gitlab.ci_pipeline_id`) are populated only from GitLab CI variables and are absent on GitHub Actions runs. + ## Diagnostics channels `code-review` publishes opt-in Node.js `diagnostics_channel` tracing events with no external telemetry dependency. Subscribers can listen before calling `run()` or from a Node preload/import hook before running the CLI. @@ -25,7 +27,7 @@ Node emits tracing subchannels as `tracing::start`, `:end`, `:asyncStart`, When `--posting-mode draft` is used, the `scm.post_comments` payload also exposes `draftsAbandoned`, `draftsCreated`, `draftsDeletedPrePublish`, and `draftsPublished` counters describing the draft lifecycle within the run. -The `git.get_merge_diff` payload exposes `diffFilesChanged`, `diffLinesAdded`, and `diffLinesRemoved`; the GitLab read phases expose `httpRequestMethod`, `httpUrl`, `httpStatusCode`, `httpResponseBodySize`, and `serverAddress` (no secrets — the token is sent in a request header, not the URL); and the top-level `run` payload exposes `postedBySeverity`, a per-severity breakdown of posted comments. +The `git.get_merge_diff` payload exposes `diffFilesChanged`, `diffLinesAdded`, and `diffLinesRemoved`; the source-control (`scm.*`) read phases expose `httpRequestMethod`, `httpUrl`, `httpStatusCode`, `httpResponseBodySize`, and `serverAddress` (no secrets — the token is sent in a request header, not the URL); and the top-level `run` payload exposes `postedBySeverity`, a per-severity breakdown of posted comments. The `reviewer.run` payload exposes a `usage` field (`{ model, tokens, cost }`) once the agent has returned. The same `usage` is forwarded onto the top-level `run` payload so a subscriber on `run:asyncEnd` sees the final token and cost totals for the review. diff --git a/docs/output-format.md b/docs/output-format.md index 8421d75..d1f82b2 100644 --- a/docs/output-format.md +++ b/docs/output-format.md @@ -4,7 +4,7 @@ ## Review output format -Reviewer output is structured so each MR review reads the same way across runs and reviewers. +Reviewer output is structured so each review reads the same way across runs, reviewers, and platforms. The same parsed output is posted to GitLab merge requests and GitHub pull requests; only the transport differs (see [How output maps to each platform](#how-output-maps-to-each-platform)). **Inline comments** use the [Conventional Comments](https://conventionalcomments.org/) shape: @@ -44,17 +44,29 @@ When there are no findings, the summary is exactly: `No issues found in the revi The Findings bullets restate only the subject of each inline comment — the discussion, impact, and fix live in the inline comment itself, not in the summary. -## MR-level summary note +## How output maps to each platform -In addition to inline discussions, the reviewer returns an overall `summary` (Markdown). The CLI posts it as a non-positional MR note — the same shape a human reviewer creates when typing in the MR comment box. The note carries a hidden marker: +The reviewer produces one platform-agnostic result — a set of inline comments and one `summary`. How it lands depends on the resolved platform: + +| | **GitLab (merge request)** | **GitHub (pull request)** | +| --------------- | -------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| Inline comments | One positional MR **discussion** per finding | One **batched PR review** (`event: COMMENT`) carrying all inline comments in one call (no per-comment rate limits); on a 422 for an off-diff position it falls back to posting comments individually, dropping only the rejected ones | +| Summary | An upserted non-positional **MR note** | An upserted **issue comment** | +| Posting modes | `direct` or `draft` (atomic bulk publish) | A single batched review (per-comment fallback on a 422); `draft` has no effect | + +The hidden fingerprint markers and the summary marker are HTML comments, invisible in the rendered view; the reviewed-commit footer is a visible `` line (`Reviewed by … for commit .`) that the commit-skip guard parses. All render identically on both platforms, so deduplication, summary upsert, and the commit-skip guard work the same way on each. + +## Summary note + +In addition to inline comments, the reviewer returns an overall `summary` (Markdown). The CLI upserts it as a single non-positional comment — a **note** on the GitLab MR (`POST` / `PUT /merge_requests/:iid/notes/:id`) or an **issue comment** on the GitHub PR — the same shape a human reviewer creates when typing in the comment box. The comment carries a hidden marker: ```md ``` -On subsequent runs the CLI finds the existing note by that marker and **updates it in place** via `PUT /merge_requests/:iid/notes/:id`, so the summary always reflects the latest review without piling up duplicates. The latest summary stays at the top of the note. When a note is updated, the previous latest summary is moved into a collapsed `
` section labeled `Previous review runs` instead of being erased; existing history is retained with a bounded limit of 10 previous runs. +On subsequent runs the CLI finds the existing comment by that marker and **updates it in place**, so the summary always reflects the latest review without piling up duplicates. The latest summary stays at the top of the comment. When it is updated, the previous latest summary is moved into a collapsed `
` section labeled `Previous review runs` instead of being erased; existing history is retained with a bounded limit of 10 previous runs. -The summary is upserted **before** inline comments are posted so it appears at the top of the MR activity feed. It appends footer metadata after a horizontal rule so reviewers can see the run cost and reviewed commit at a glance: +The summary is upserted **before** inline comments are posted so it appears at the top of the review activity feed. It appends footer metadata after a horizontal rule so reviewers can see the run cost and reviewed commit at a glance: ```md --- @@ -68,7 +80,7 @@ Reviewed by [@weareikko/code-review](https://github.com/weareikko/code-review) f The `Review usage:` line names the model and records the `--thinking` level the run used (`thinking: off` by default). The `Skills:` line is only present when one or more skills were active for the run. -If a later CI job sees that the current MR head commit already appears in that footer, it skips the agent run to avoid producing a different review for the same diff. Use `--force-review` or `CODE_REVIEW_FORCE_REVIEW=true` to bypass the guard. The summary upsert runs in both `direct` and `draft` posting modes (it always uses the regular notes endpoints — the atomic bulk-publish flow is reserved for inline comments). +If a later CI job sees that the current head commit (of the MR or PR) already appears in that footer, it skips the agent run to avoid producing a different review for the same diff. Use `--force-review` or `CODE_REVIEW_FORCE_REVIEW=true` to bypass the guard. On GitLab the summary upsert runs in both `direct` and `draft` posting modes (it always uses the regular notes endpoints — the atomic bulk-publish flow is reserved for inline comments). Disable with `--no-summary` or `CODE_REVIEW_POST_SUMMARY=false`. With `--dry-run`/`--no-post`, the summary is parsed but not posted, and the reviewed-commit skip guard is not applied. @@ -93,4 +105,6 @@ Each generated comment body includes hidden markers: ``` -Before posting, the CLI fetches existing MR discussions and skips comments where either fingerprint is already present. This prevents reposting across reruns and also prevents duplicates generated in the same run. +Before posting, the CLI fetches the existing review discussions (MR discussions on GitLab, the PR's review and issue comments on GitHub) and skips comments where either fingerprint is already present. This prevents reposting across reruns and also prevents duplicates generated in the same run. + +The fingerprint markers, the summary marker (``), and the reviewed-commit footer are read backward-compatibly: comments posted under the tool's former identity (the `gitlab-review:` marker prefix and the previous repository/product name in the footer) are still matched, so the first run after upgrading upserts and deduplicates against them rather than posting duplicates. diff --git a/docs/providers.md b/docs/providers.md index 6cf2290..439e07d 100644 --- a/docs/providers.md +++ b/docs/providers.md @@ -66,7 +66,7 @@ npx @weareikko/code-review \ Behaviour: -- **Fixed angle→model mapping.** Angle `i` runs on pool member `i % pool.length`. The mapping is deterministic and stable for a given (MR, commit) — there is no randomness or round-robin. +- **Fixed angle→model mapping.** Angle `i` runs on pool member `i % pool.length`. The mapping is deterministic and stable for a given (MR/PR, commit) — there is no randomness or round-robin. - **Cross-family verifier.** Each severe finding is verified by a pool member _other than_ the model that authored it (with 3+ members the tie-break is deterministic by pool order; with a single-model pool it degenerates to today's behaviour). The author-model annotation is internal pipeline metadata only — it never reaches a posted comment, fingerprint, or the summary. - **Per-stage key resolution.** Each member resolves its own provider key from that provider's standard env var, so a key for one provider is never sent to another. A member whose key is missing or empty is dropped with a warning instead of failing the run; if every member is unusable, the pipeline falls back to `--model` (which is already validated to have a key). - **Per-model cost breakdown.** When more than one distinct model runs, the `Review usage:` output adds a per-model breakdown (tokens and cost per pool member) and `review-usage.json` records the same under a `byModel` array. The top-level totals are the sum across the pool. diff --git a/docs/skills.md b/docs/skills.md index 3049f29..af28bc7 100644 --- a/docs/skills.md +++ b/docs/skills.md @@ -89,7 +89,7 @@ Project skills take precedence over built-in skills with the same name. A skill ## Skills footer -When skills are active, their names appear in the MR summary note footer: +When skills are active, their names appear in the review summary footer (the MR note on GitLab, the PR summary comment on GitHub): ```md Skills: `code-review`