Skip to content

ci(review): use Anthropic workload identity federation, fall back to the stored key - #700

Merged
ajianaz merged 1 commit into
developfrom
ci/cora-review-wif
Oct 27, 2026
Merged

ajianaz merged 1 commit into
developfrom
ci/cora-review-wif

Conversation

@ajianaz

@ajianaz ajianaz commented Oct 27, 2026

Copy link
Copy Markdown
Collaborator

What

  • The Cora Review job exchanges its GitHub OIDC token for a short-lived Anthropic access token when the four federation identifiers (ANTHROPIC_FEDERATION_RULE_ID, ANTHROPIC_ORGANIZATION_ID, ANTHROPIC_SERVICE_ACCOUNT_ID, ANTHROPIC_WORKSPACE_ID) are available as organization or repository secrets or variables. Model: ANTHROPIC_MODEL, default claude-opus-5-5.
  • With a federation token, API key, base URL and model switch together: the token is only ever sent to https://api.anthropic.com/v1, never to CORA_BASE_URL.
  • Without one (variables missing, or the exchange fails: it is only a warning) the job uses CORA_API_KEY, CORA_BASE_URL and CORA_MODEL as before, and the guard still fails with an error naming a missing secret.
  • id-token: write added to the job permissions; docs (docs/examples.md) and CHANGELOG updated.

Why

A stored API key can leak and does not expire; the exchanged token lives for one job. The exchange and a Bearer call to /v1/chat/completions (the way cora sends its key) were verified by the smoke test in the previous PR.

Testing

  • The smoke workflow passed: token exchange HTTP 200, /v1/chat/completions with Bearer HTTP 200.
  • Not testable in this PR: the Cora Review job runs from the base branch for pull_request_target, so this change takes effect on the next PR after merge. The fallback keeps the required check working if the exchange fails there (the subject for pull_request_target may differ from pull_request).

…the stored key

Signed-off-by: ajianaz <ajianaz@users.noreply.github.com>
@ajianaz
ajianaz merged commit 7991466 into develop Oct 27, 2026
13 checks passed
ajianaz added a commit that referenced this pull request Oct 27, 2026
## What

- A 400 response that names `temperature` is retried once without that
key, and the model is remembered for the rest of the process (no
repeated failing request, and no extra cost on later chunks). Streaming
and non-streaming share the same path.
- Any other 400 (and every other status) is returned unchanged;
providers that accept `temperature` keep the configured value (default
`0.0`, deterministic).

## Why

`claude-opus-5-5` answers HTTP 400 "`temperature` is deprecated for this
model", so every review through the Anthropic workload identity
federation path (#700) ended as "Review could not complete" while the
check still turned green. Closes #701.

## Testing

- Unit tests: request body without a `temperature` key, recognising the
400, per-model memory.
- Two end-to-end tests against a local fake server: the first request
carries `temperature`, the retry does not and succeeds; a different 400
is not retried (one request only).
- fmt, clippy -D warnings and the full suite pass.

Signed-off-by: ajianaz <ajianaz@users.noreply.github.com>
Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
ajianaz added a commit that referenced this pull request Oct 27, 2026
…the stored key (#700)

## What

- The Cora Review job exchanges its GitHub OIDC token for a short-lived
Anthropic access token when the four federation identifiers
(`ANTHROPIC_FEDERATION_RULE_ID`, `ANTHROPIC_ORGANIZATION_ID`,
`ANTHROPIC_SERVICE_ACCOUNT_ID`, `ANTHROPIC_WORKSPACE_ID`) are available
as organization or repository secrets or variables. Model:
`ANTHROPIC_MODEL`, default `claude-opus-5-5`.
- With a federation token, API key, base URL and model switch together:
the token is only ever sent to `https://api.anthropic.com/v1`, never to
`CORA_BASE_URL`.
- Without one (variables missing, or the exchange fails: it is only a
warning) the job uses `CORA_API_KEY`, `CORA_BASE_URL` and `CORA_MODEL`
as before, and the guard still fails with an error naming a missing
secret.
- `id-token: write` added to the job permissions; docs
(`docs/examples.md`) and CHANGELOG updated.

## Why

A stored API key can leak and does not expire; the exchanged token lives
for one job. The exchange and a Bearer call to `/v1/chat/completions`
(the way cora sends its key) were verified by the smoke test in the
previous PR.

## Testing

- The smoke workflow passed: token exchange HTTP 200,
`/v1/chat/completions` with Bearer HTTP 200.
- Not testable in this PR: the Cora Review job runs from the base branch
for `pull_request_target`, so this change takes effect on the next PR
after merge. The fallback keeps the required check working if the
exchange fails there (the subject for `pull_request_target` may differ
from `pull_request`).

Signed-off-by: ajianaz <ajianaz@users.noreply.github.com>
Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
ajianaz added a commit that referenced this pull request Oct 27, 2026
## What

- A 400 response that names `temperature` is retried once without that
key, and the model is remembered for the rest of the process (no
repeated failing request, and no extra cost on later chunks). Streaming
and non-streaming share the same path.
- Any other 400 (and every other status) is returned unchanged;
providers that accept `temperature` keep the configured value (default
`0.0`, deterministic).

## Why

`claude-opus-5-5` answers HTTP 400 "`temperature` is deprecated for this
model", so every review through the Anthropic workload identity
federation path (#700) ended as "Review could not complete" while the
check still turned green. Closes #701.

## Testing

- Unit tests: request body without a `temperature` key, recognising the
400, per-model memory.
- Two end-to-end tests against a local fake server: the first request
carries `temperature`, the retry does not and succeeds; a different 400
is not retried (one request only).
- fmt, clippy -D warnings and the full suite pass.

Signed-off-by: ajianaz <ajianaz@users.noreply.github.com>
Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
ajianaz added a commit to codecoradev/tole that referenced this pull request Oct 28, 2026
…the stored key (#449)

## What

The Cora Review job exchanges its GitHub OIDC token for a short-lived
Anthropic access token (workload identity federation) when
`ANTHROPIC_FEDERATION_RULE_ID`, `ANTHROPIC_ORGANIZATION_ID`,
`ANTHROPIC_SERVICE_ACCOUNT_ID` and `ANTHROPIC_WORKSPACE_ID` are
available. Without them, or when the exchange fails, it uses the
`CORA_*` secrets as before.

Closes #448. Ported from codecoradev/cora-code#700.

## Why

A stored API key can leak and does not expire; the exchanged token lives
for one job.

## Changes

- `.github/workflows/cora-review.yml`: `id-token: write`; an exchange
step (`continue-on-error`, warning only) that keeps the OIDC token in
its own shell and exports only the short-lived token; a guard that fails
the job when neither federation nor the `CORA_*` secrets are available;
with a federation token, key, base URL (`https://api.anthropic.com/v1`)
and model (`ANTHROPIC_MODEL`, default `claude-opus-5-5`) switch
together, so the token never goes to `CORA_BASE_URL`.
- `.github/workflows/anthropic-wif-smoke.yml`: manual
(`workflow_dispatch`) check of the exchange and a Bearer call to
`/v1/chat/completions`; prints HTTP statuses only.
- `CONTRIBUTING.md`: credential setup, including the numeric-id subject
prefix gotcha.

## Testing

- YAML parses; Cora agent review (prepare + finalize): 0 findings,
`should_block: false`.
- This repository's job runs on `pull_request`, so this PR's own Cora
Review run exercises the new workflow: the federated path if the
Anthropic federation rule allows
`repo:codecoradev@<id>/tole@<id>:pull_request`, otherwise the fallback
with a warning.

---------

Co-authored-by: ajianaz <ajianaz@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant