Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
14 commits
Select commit Hold shift + click to select a range
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
3 changes: 3 additions & 0 deletions .github/workflows/codeboarding-sync.yml
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,9 @@ jobs:
- uses: ./
with:
mode: sync
# CodeBoarding's own repositories run on the CodeBoarding plan.
llm: license

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

this is fine no need to change it. But tbh it sounds like it should be "provider"

@Svilen-Stefanov Svilen-Stefanov Aug 27, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

We use provider often for the LLM provider (Anthropic etc.) so provider is probably not the best word either but maybe tier? Nvm, tier doesn't quite work either. I'll keep it for now.

license_key: ${{ secrets.CODEBOARDING_LICENSE }}
sync_strategy: ${{ inputs.sync_strategy || 'push' }}
force_full: ${{ inputs.force_full || false }}
# App token authenticates the baseline push so the commit is attributed
Expand Down
5 changes: 5 additions & 0 deletions .github/workflows/codeboarding.yml
Original file line number Diff line number Diff line change
Expand Up @@ -111,4 +111,9 @@ jobs:
echo "::warning::CodeBoarding GitHub App token is unavailable; falling back to github-actions[bot]. Check CODEBOARDING_APP_PRIVATE_KEY formatting if app credentials are configured."
- uses: ./
with:
# Named, not inferred: the action refuses to guess where credentials come
# from, so every workflow says which of hosted / license / a provider it
# wants. CodeBoarding's own repositories run on the CodeBoarding plan.
llm: license
license_key: ${{ secrets.CODEBOARDING_LICENSE }}
github_token: ${{ steps.codeboarding-app-token-client.outputs.token || steps.codeboarding-app-token-app.outputs.token || github.token }}
2 changes: 2 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ jobs:
exit 1
fi
python -m pip install --disable-pip-version-check "$requirement"
- name: Check the provider table against the installed release
run: python -m unittest tests.test_provider_table_drift -v
- name: Render with the installed CodeBoarding release
run: >-
python scripts/diff_to_mermaid.py
Expand Down
26 changes: 26 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,19 @@ pinned to a release in `action.yml`. `scripts/engine_adapter.py` is the CLI
adapter into it (no analysis logic lives there). Engine changes reach users only
when that pin is bumped *and* a new action release ships.

## Bumping the engine pin

`scripts/action/supported-providers.json` mirrors the pinned release's `LLM_PROVIDERS`.
Credentials are validated before the engine is installed — that is what makes a
misconfigured run fail in seconds instead of a minute — so the action cannot ask
the engine at run time and keeps this copy instead.

When you change the `codeboarding==` pin in `action.yml`, update that file in the
same commit: its `engine` field, and any provider whose selection variables
changed. `tests/test_provider_table_drift.py` runs in the `core-compatibility` CI
job and fails when they disagree. Adding a provider also means adding its
`<name>_api_key` input to `action.yml`; `tests/test_action_inputs.py` checks that.

## Protected tests

Some tests encode a behavioural contract that is expensive to rediscover once
Expand All @@ -35,6 +48,12 @@ Protected tests:
its merge base, never against the base branch tip. Comparing against the tip
attributes other people's commits to the pull request and reports them
backwards, as removals.
- `tests/test_action_auth.py::test_a_named_provider_never_falls_back_to_codeboarding_credentials`
— a workflow that names a provider runs on that provider or fails. The action
used to read an empty key as "no preference" and resolve it to CodeBoarding's
hosted tier, so a repository that had not added its secret yet went green
while running on another vendor's model and CodeBoarding's money, silently.
Any change that reintroduces a credential fallback breaks this test.

## Releases

Expand All @@ -56,6 +75,13 @@ messages, so every commit and PR title must follow Conventional Commits:
- `fix:` → patch bump
- `feat!:` / `fix!:` or a `BREAKING CHANGE:` footer → major bump. Avoid unless
intended: consumers pinned to the old major tag never receive it automatically.

A deliberate exception exists. The explicit-credentials change (`llm` required,
no fallback) is a breaking change that shipped as `feat:`, not `feat!:`. A major
bump moves adopters to `v2` and freezes `v1`, which would have left every
existing workflow on the old silent-fallback behaviour forever — the opposite of
the intent. Shipping it as a minor bump on the moving `v1` tag is what makes
adopters actually receive it. Do not "correct" this to `feat!:` after the fact.
- `chore:` / `docs:` / `ci:` / `refactor:` / `test:` → ride along in the next
release but do not trigger one.

Expand Down
122 changes: 99 additions & 23 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,9 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- uses: CodeBoarding/CodeBoarding-action@v2
- uses: CodeBoarding/CodeBoarding-action@v1

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

that is really good catch, there is no v2 💀

with:
llm: hosted # or license, or a provider name -- see Authentication
```

Automatic runs update one sticky **CodeBoarding review** comment. A trusted repository owner, member, or collaborator can comment `/codeboarding` to analyze the current PR head again, including on fork PRs; every command creates a new result comment.
Expand Down Expand Up @@ -98,29 +100,96 @@ Fork pull requests never carry an analysis forward. They are reviewed on request

## Authentication and providers

With no LLM inputs, the action uses CodeBoarding's hosted OpenRouter tier. It mints short-lived GitHub OIDC credentials per request, so the job needs `id-token: write` and no stored LLM secret.

For a direct provider, pass its name and key:
The `llm` input is required and says where analysis credentials come from. There are
three answers, and the action never picks one for you:

```yaml
- uses: CodeBoarding/CodeBoarding-action@v2
with:
llm_provider: anthropic
llm_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
with:
llm: hosted # CodeBoarding's free tier
```
```yaml
with:
llm: license # a CodeBoarding plan
license_key: ${{ secrets.CODEBOARDING_LICENSE }}
```
```yaml
with:
llm: anthropic # your own provider key
anthropic_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
```

The action maps a provider name to the environment variable Core uses (`anthropic` → `ANTHROPIC_API_KEY`, `openai` → `OPENAI_API_KEY`, and so on). `aws`/`aws_bedrock` maps to `AWS_BEARER_TOKEN_BEDROCK`. Provider names following the standard convention are not restricted by an action-side allowlist; the pinned Core release remains the source of truth for which providers it implements.

CodeBoarding 0.13.10 supports OpenRouter, OpenAI-compatible endpoints, Anthropic-compatible endpoints, Google, Vercel AI Gateway, AWS Bedrock, Cerebras, DeepSeek, GLM, Kimi, OrcaRouter, Ollama, and LiteLLM. See Core's [`agents/llm_config.py`](https://github.com/CodeBoarding/CodeBoarding/blob/main/agents/llm_config.py) for current defaults and endpoint variables. In particular, Ollama needs `OLLAMA_BASE_URL` or `OLLAMA_HOST`, and LiteLLM needs `LITELLM_BASE_URL` on the action step.
`hosted` and `license` run through CodeBoarding's proxy and need `id-token: write`, which
mints short-lived credentials per request and stores no LLM secret in your repository. A
provider key is used directly and needs no OIDC permission.

A CodeBoarding license keeps the hosted OIDC path but removes hosted quota limits:
**An empty value is never a fallback.** If you name a provider and its key is missing --
because the secret does not exist yet, or is misspelt — the run fails in its first
seconds and says which input and which secret to fix. It does not quietly analyze on
CodeBoarding's hosted tier instead. That was the old behaviour, and it meant a repository
could report an Anthropic review that Anthropic never produced.

```yaml
with:
license_key: ${{ secrets.CODEBOARDING_LICENSE }}
```
The same rule makes the combinations explicit rather than order-dependent:

`llm_api_key` takes precedence over `license_key`. A direct provider key does not require `id-token: write`; hosted free and licensed usage does.
| Workflow says | Result |
|---|---|
| nothing | refused: `llm` is required |
| `llm: hosted` | the free tier |
| `llm: hosted` + any provider key | refused: pick one |
| `llm: hosted` + `license_key` | refused: use `llm: license` |
| `llm: license` without `license_key` | refused: names the secret to add |
| `llm: anthropic` + `anthropic_api_key` | Anthropic, directly |
| `llm: anthropic`, key empty or absent | refused: names the input and the secret |
| `llm: anthropic` + `openai_api_key` | refused: a second provider's key |
| `llm: anthropic` + key + `license_key` | Anthropic, on a CodeBoarding plan |

A licence alongside your own key is deliberately allowed: it says "my CodeBoarding plan,
my own tokens". **Your key always wins.** A direct provider call never reaches
CodeBoarding, so the licence is recorded and reported but not spent, and nothing meters
that combination today. The job summary says so on every run, rather than leaving you to
infer it from the tier name.

### Providers

Each provider has its own inputs, so which key a workflow uses is readable from the file
without knowing any precedence rules.

| `llm` | Provider | Inputs | Needs at least one of |
|---|---|---|---|
| `anthropic` | Anthropic | `anthropic_api_key` | `anthropic_api_key` |
| `aws_bedrock` | AWS Bedrock | `aws_bedrock_api_key`, `aws_bedrock_region` | `aws_bedrock_api_key` |
| `cerebras` | Cerebras | `cerebras_api_key` | `cerebras_api_key` |
| `deepseek` | DeepSeek | `deepseek_api_key`, `deepseek_base_url` | `deepseek_api_key` or `deepseek_base_url` |
| `glm` | GLM | `glm_api_key`, `glm_base_url` | `glm_api_key` or `glm_base_url` |
| `google` | Google Gemini | `google_api_key` | `google_api_key` |
| `kimi` | Kimi | `kimi_api_key`, `kimi_base_url` | `kimi_api_key` or `kimi_base_url` |
| `litellm` | LiteLLM | `litellm_api_key`, `litellm_base_url` | `litellm_base_url` |
| `ollama` | Ollama | `ollama_api_key`, `ollama_base_url` | `ollama_base_url` |
| `openai` | OpenAI | `openai_api_key`, `openai_base_url` | `openai_api_key` or `openai_base_url` |
| `openrouter` | OpenRouter | `openrouter_api_key` | `openrouter_api_key` |
| `orcarouter` | OrcaRouter | `orcarouter_api_key` | `orcarouter_api_key` |
| `vercel` | Vercel AI Gateway | `vercel_api_key`, `vercel_base_url` | `vercel_api_key` or `vercel_base_url` |

Each provider has exactly one accepted spelling, and its inputs are named after it, so
`llm: X` always pairs with `X_api_key`. There are no aliases: a second spelling is another
thing to document and keep in step, and an unrecognised value is refused with the accepted
list. `ollama` and `litellm` are selected by their endpoint rather than a key, which is why
a key alone does not configure them — that mirrors how Core itself decides.

This table is generated from [`scripts/action/supported-providers.json`](scripts/action/supported-providers.json),
which mirrors the CodeBoarding release this action pins. `tests/test_provider_table_drift.py`
installs that release in CI and fails if the two disagree, so a provider cannot be added
to Core and silently stay unreachable here.

### Reporting

Every run reports what it resolved, so the answer never has to be inferred from behaviour:

- outputs `llm_tier` (`hosted`, `license`, `byok`, `byok+license`), `llm_provider` (empty
on the hosted tiers, where the upstream is ours rather than yours), and
`llm_config_error` (empty when configured);
- a job-summary table naming the tier and provider;
- on a configuration failure, an error annotation and — in review mode — a pull request
comment with the fix, so the person who has to add the secret sees it where they are.

## Model selection

Expand Down Expand Up @@ -189,9 +258,10 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 60
steps:
- uses: CodeBoarding/CodeBoarding-action@v2
- uses: CodeBoarding/CodeBoarding-action@v1
with:
mode: sync
llm: hosted
target_branch: main
force_full: ${{ inputs.force_full || false }}
```
Expand All @@ -209,9 +279,10 @@ permissions:
id-token: write

# ...
- uses: CodeBoarding/CodeBoarding-action@v2
- uses: CodeBoarding/CodeBoarding-action@v1
with:
mode: sync
llm: hosted
target_branch: main
sync_strategy: pull_request
```
Expand All @@ -225,9 +296,11 @@ With the default `github.token`, the repository or organization must allow GitHu
| Input | Mode | Default | Description |
|---|---|---|---|
| `mode` | both | `review` | `review` or `sync`. |
| `llm_api_key` | both | empty | Direct-provider key. With the default provider, empty selects hosted OIDC usage. |
| `llm_provider` | both | `openrouter` | Provider for `llm_api_key`. |
| `license_key` | both | empty | License for unmetered hosted usage. |
| `llm` | both | **required** | `hosted`, `license`, or a provider name. No default. |
| `<provider>_api_key` | both | empty | That provider's key, e.g. `anthropic_api_key`. See [Providers](#providers). |
| `<provider>_base_url` | both | empty | That provider's endpoint, where it has one. |
| `aws_bedrock_region` | both | empty | Bedrock region. Core defaults to `us-east-1`. |
| `license_key` | both | empty | CodeBoarding license. Required by `llm: license`. |
| `model` | both | empty | Default model for both analysis and parsing. |
| `agent_model` | both | empty | Analysis-only override for `model`. |
| `parsing_model` | both | empty | Parsing-only override for `model`. |
Expand All @@ -237,12 +310,15 @@ With the default `github.token`, the repository or organization must allow GitHu
| `force_full` | sync | `false` | Ignore the committed baseline for this run. |
| `warmstart_retention_days` | review | `1` | Days to keep the reusable analysis. Only the next run reads it. |

The `/codeboarding` command, comment heading, Mermaid direction (`LR`), hosted webview URL, rolling sync branch, commit message, and CodeBoarding 0.13.10 version are intentionally fixed in v2 rather than exposed as configuration.
The `/codeboarding` command, comment heading, Mermaid direction (`LR`), hosted webview URL, rolling sync branch, commit message, and CodeBoarding 0.13.10 version are intentionally fixed rather than exposed as configuration.

## Outputs

| Output | Mode | Description |
|---|---|---|
| `llm_tier` | both | `hosted`, `license`, `byok`, or `byok+license`. |
| `llm_provider` | both | Provider the run used. Empty on `hosted` and `license`: which upstream the proxy routes to is CodeBoarding's decision, not your configuration. |
| `llm_config_error` | both | Configuration failure code, empty when configured. |
| `diagram_md` | review | Path to the rendered Mermaid block on the runner. |
| `n_changed` | review | Number of changed components. |
| `truncated` | review | Whether the graph was reduced to fit GitHub limits. |
Expand Down
Loading
Loading