diff --git a/README.md b/README.md
index e2cb55e..5c627ba 100644
--- a/README.md
+++ b/README.md
@@ -1,37 +1,57 @@
-
-

+# CodeBoarding Review
- # CodeBoarding Visual Architecture Review
+Review system design on every pull request, not just the diff.
- Visual system-design review for pull requests. CodeBoarding analyzes the architecture before and after a change, then comments on the PR with an inline Mermaid diagram showing what changed.
-
+CodeBoarding analyzes your architecture before and after a change, then comments on the PR with an inline Mermaid diagram of what changed — added, modified, and deleted components and the relationships between them. It runs the [CodeBoarding](https://github.com/CodeBoarding/CodeBoarding) engine in CI: static analysis combined with LLM reasoning.
-## What It Does
+[CodeBoarding](https://github.com/CodeBoarding/CodeBoarding) · [Website](https://codeboarding.org) · [Explore examples](https://codeboarding.org/diagrams) · [VS Code extension](https://marketplace.visualstudio.com/items?itemName=Codeboarding.codeboarding) · [Discord](https://discord.gg/T5zHTJYFuy)
+
+[](https://developer.mozilla.org/en-US/docs/Web/JavaScript)
+[](https://www.typescriptlang.org/)
+[](https://www.java.com/)
+[](https://www.python.org/)
+[](https://go.dev/)
+[](https://www.php.net/)
+[](https://www.rust-lang.org/)
+[](https://learn.microsoft.com/en-us/dotnet/csharp/)
+
+## What it does
- Builds or reuses a baseline architecture analysis for the PR base.
- Runs incremental analysis on the PR head, then diffs components and relationships.
-- Posts a sticky PR comment with an inline Mermaid map — 🟩 added · 🟨 modified · 🟥 deleted (dashed), for both nodes and edges.
+- Posts a sticky PR comment with an inline Mermaid map. Green is added, yellow is modified, red (dashed) is deleted, for both nodes and edges.
A PR comment looks like this:
```mermaid
graph LR
- Gateway["API Gateway"]
- Auth["Auth Service"]
- Cache["Cache"]
- Gateway -- "routes to" --> Auth
- Auth -- "reads/writes" --> Cache
+ Orchestration_Workflow_Manager["Orchestration & Workflow Manager"]
+ Incremental_Analysis_Controller["Incremental Analysis Controller"]
+ Static_Analysis_Engine["Static Analysis Engine"]
+ Agentic_Intelligence_Core["Agentic Intelligence Core"]
+ Health_Quality_Monitor["Health & Quality Monitor"]
+ Rendering_Output_Engine["Rendering & Output Engine"]
+ Persistence_Provider_Infrastructure["Persistence & Provider Infrastructure"]
+ Orchestration_Workflow_Manager -- "triggers change detection" --> Incremental_Analysis_Controller
+ Incremental_Analysis_Controller -- "passes filtered file sets" --> Static_Analysis_Engine
+ Static_Analysis_Engine -- "provides CFGs and symbol tables" --> Agentic_Intelligence_Core
+ Static_Analysis_Engine -- "supplies structural metrics" --> Health_Quality_Monitor
+ Agentic_Intelligence_Core -- "delivers summaries and diagrams" --> Rendering_Output_Engine
+ Health_Quality_Monitor -- "provides health reports" --> Rendering_Output_Engine
+ Persistence_Provider_Infrastructure -- "supplies LLM clients" --> Agentic_Intelligence_Core
+ Orchestration_Workflow_Manager -- "persists pipeline state" --> Persistence_Provider_Infrastructure
classDef added fill:#1f883d,stroke:#0b5d23,color:#fff;
classDef modified fill:#bf8700,stroke:#7d4e00,color:#fff;
classDef deleted fill:#cf222e,stroke:#82071e,color:#fff,stroke-dasharray:5 3;
- class Cache added;
- class Auth modified;
- class Gateway deleted;
- linkStyle 0 stroke:#cf222e,stroke-width:2px,stroke-dasharray:5 3;
- linkStyle 1 stroke:#1f883d,stroke-width:2px;
+ class Health_Quality_Monitor added;
+ class Static_Analysis_Engine,Agentic_Intelligence_Core modified;
+ class Persistence_Provider_Infrastructure deleted;
+ linkStyle 3,5 stroke:#1f883d,stroke-width:2px;
+ linkStyle 2 stroke:#bf8700,stroke-width:2px;
+ linkStyle 6,7 stroke:#cf222e,stroke-width:2px,stroke-dasharray:5 3;
```
-## Usage
+## Quick start
Create `.github/workflows/codeboarding.yml`:
@@ -40,7 +60,7 @@ name: CodeBoarding review
on:
pull_request:
- # Generate ONCE, when the PR becomes reviewable — not on every push, so you
+ # Generate once, when the PR becomes reviewable, not on every push, so you
# don't spend an LLM job per commit. Use [opened] for strictly creation-only,
# or add `synchronize` to re-run on each push. Refresh anytime with /codeboarding.
types: [opened, reopened, ready_for_review]
@@ -70,15 +90,15 @@ jobs:
llm_api_key: ${{ secrets.OPENROUTER_API_KEY }}
```
-Add the API key as a repository secret (**Settings → Secrets and variables → Actions**):
+Add the API key as a [repository secret](https://docs.github.com/en/actions/how-tos/write-workflows/choose-what-workflows-do/use-secrets) (Settings → Secrets and variables → Actions):
```text
OPENROUTER_API_KEY = sk-or-...
```
-That's the only required setup — it's passed via `llm_api_key` above. (For local runs with `scripts/run_local.sh`, export `OPENROUTER_API_KEY` as an env var instead.)
+That is the only required setup, passed via `llm_api_key` above. For local runs with `scripts/run_local.sh`, export `OPENROUTER_API_KEY` as an environment variable instead.
-**Models are optional.** Omit `agent_model` / `parsing_model` to use the engine's default for your provider, or pin them — inline or from a repository **variable** (a model name isn't a secret, so use `vars.`, not `secrets.`):
+Models are optional. Omit `agent_model` and `parsing_model` to use the engine's default for your provider, or pin them inline or from a repository variable (a model name is not a secret, so use `vars.`, not `secrets.`):
```yaml
with:
@@ -87,11 +107,9 @@ That's the only required setup — it's passed via `llm_api_key` above. (For loc
parsing_model: google/gemini-3-flash-preview # optional
```
-**Model format (OpenRouter):** a bare OpenRouter slug (e.g. `anthropic/claude-sonnet-4`) — exactly one `/`, **no `openrouter/` prefix** (that's the LiteLLM form; the action rejects it early). Other providers use their own native model ids.
-
## Bring your own LLM provider
-OpenRouter is the default, but you can use any provider the engine supports — set `llm_provider` and pass that provider's key:
+OpenRouter is the default, but you can use any provider the engine supports. Set `llm_provider` and pass that provider's key:
```yaml
with:
@@ -99,13 +117,13 @@ OpenRouter is the default, but you can use any provider the engine supports —
llm_api_key: ${{ secrets.ANTHROPIC_API_KEY }}
```
-`llm_provider: ` hands your key to the engine as `_API_KEY`, and the engine auto-selects that provider. Set **exactly one** key per run.
+`llm_provider: ` hands your key to the engine as `_API_KEY`, and the engine auto-selects that provider. Set exactly one key per run.
-Supported providers
+Supported providers
-| `llm_provider` | env var the engine reads |
+| `llm_provider` | Environment variable the engine reads |
|---|---|
-| `openrouter` *(default)* | `OPENROUTER_API_KEY` |
+| `openrouter` (default) | `OPENROUTER_API_KEY` |
| `openai` | `OPENAI_API_KEY` |
| `anthropic` | `ANTHROPIC_API_KEY` |
| `google` | `GOOGLE_API_KEY` |
@@ -116,31 +134,31 @@ OpenRouter is the default, but you can use any provider the engine supports —
| `aws_bedrock` | `AWS_BEARER_TOKEN_BEDROCK` |
| `ollama` | `OLLAMA_BASE_URL` |
-This table mirrors the engine and may lag it — the source of truth is the engine's provider registry ([`agents/llm_config.py`](https://github.com/CodeBoarding/CodeBoarding/blob/main/agents/llm_config.py)). Any provider it adds that follows the `_API_KEY` convention works here with no action change.
+This table mirrors the engine and may lag it. The source of truth is the engine's provider registry, [`agents/llm_config.py`](https://github.com/CodeBoarding/CodeBoarding/blob/main/agents/llm_config.py). Any provider it adds that follows the `_API_KEY` convention works here with no action change.
## When it runs
-- **PR opened / reopened / marked ready** — generated once (per the `on:` triggers above). It does **not** re-run on every push, so you never spend an LLM job per commit; the comment reflects that point until refreshed.
-- **`/codeboarding` comment** — a trusted collaborator (`OWNER`/`MEMBER`/`COLLABORATOR`) regenerates the diagram against the **current** PR head, even if one already exists. It re-runs and updates the same comment in place (the action reacts with 👀). Change the keyword via `trigger_command`.
+- On a PR being opened, reopened, or marked ready for review, the diagram is generated once (per the `on:` triggers above). It does not re-run on every push, so you never spend an LLM job per commit; the comment reflects that point until refreshed.
+- On a `/codeboarding` comment, a trusted collaborator (`OWNER`, `MEMBER`, or `COLLABORATOR`) regenerates the diagram against the current PR head, even if one already exists. It re-runs and updates the same comment in place. Change the keyword via `trigger_command`.
-The command needs the `issue_comment` trigger and runs from your **default branch** (GitHub's rule), so it only works once the workflow is merged there. On-demand runs on fork PRs are refused, so fork code is never analyzed with your secrets.
+The command needs the `issue_comment` trigger and runs from your default branch (a GitHub rule), so it only works once the workflow is merged there. On-demand runs on fork PRs are refused, so fork code is never analyzed with your secrets.
## Inputs
| Input | Default | Description |
|---|---|---|
| `llm_api_key` | required | Your LLM provider API key (see `llm_provider`). |
-| `llm_provider` | `openrouter` | Provider for the key — mapped to `_API_KEY` (e.g. `anthropic`, `openai`, `google`). |
-| `github_token` | `${{ github.token }}` | Token used to post/update the PR comment. |
+| `llm_provider` | `openrouter` | Provider for the key, mapped to `_API_KEY` (e.g. `anthropic`, `openai`, `google`). |
+| `github_token` | `${{ github.token }}` | Token used to post or update the PR comment. |
| `engine_ref` | `v0.12.0` | CodeBoarding engine ref. Pin for reproducibility. |
| `depth_level` | `1` | Analysis depth, 1 to 3. Higher is slower and richer. |
| `render_depth` | `1` | Display depth for the PR diagram. Keep `1` for a clean top-level view. |
| `diagram_direction` | `LR` | Mermaid direction: `LR`, `TD`, `TB`, `RL`, or `BT`. |
| `changed_only` | `false` | Render only changed components and incident edges. |
-| `agent_model` | engine default | Analysis model. Bare OpenRouter slug (e.g. `anthropic/claude-sonnet-4`); empty = engine's per-provider default. |
-| `parsing_model` | engine default | Parsing model. Bare OpenRouter slug; empty = engine's per-provider default. |
+| `agent_model` | `google/gemini-3-flash-preview` | Analysis model. OpenRouter default shown; other providers use their own engine default. |
+| `parsing_model` | `google/gemini-3.1-flash-lite-preview` | Parsing model. OpenRouter default shown; other providers use their own engine default. |
| `comment_header` | `Architecture review` | Heading for the PR comment. |
| `trigger_command` | `/codeboarding` | Slash command for trusted on-demand runs. |
| `cta_base_url` | empty | Optional click-proxy base URL for editor and extension links. |
@@ -160,7 +178,7 @@ The command needs the `issue_comment` trigger and runs from your **default branc
- Do not use `pull_request_target` for this action. It can expose secrets to PR-head code.
- GitHub renders Mermaid in strict mode, so node click-through links are not supported in the PR diagram.
-## Local Testing
+## Local testing
Fast path, no LLM calls:
diff --git a/action.yml b/action.yml
index 24bcf5f..a142d58 100644
--- a/action.yml
+++ b/action.yml
@@ -27,11 +27,11 @@ inputs:
required: false
default: '1'
agent_model:
- description: 'Analysis model (AGENT_MODEL env var). A bare OpenRouter slug, e.g. anthropic/claude-sonnet-4. Empty (default) uses the engine''s own per-provider default.'
+ description: 'Analysis model (AGENT_MODEL env var). A bare OpenRouter slug. Defaults to google/gemini-3-flash-preview on OpenRouter; for other providers, empty uses the engine''s per-provider default.'
required: false
default: ''
parsing_model:
- description: 'Parsing model (PARSING_MODEL env var). A bare OpenRouter slug. Empty (default) uses the engine''s own per-provider default.'
+ description: 'Parsing model (PARSING_MODEL env var). A bare OpenRouter slug. Defaults to google/gemini-3.1-flash-lite-preview on OpenRouter; for other providers, empty uses the engine''s per-provider default.'
required: false
default: ''
comment_header:
@@ -155,6 +155,21 @@ runs:
gh api -X POST "repos/${REPOSITORY}/issues/comments/${COMMENT_ID}/reactions" \
-f content=eyes >/dev/null 2>&1 || true
+ - name: Post in-progress comment
+ if: steps.guard.outputs.skip != 'true'
+ continue-on-error: true
+ uses: marocchino/sticky-pull-request-comment@v2
+ with:
+ header: codeboarding-architecture-diff
+ number: ${{ steps.guard.outputs.pr_number }}
+ message: |
+ ### ${{ inputs.comment_header }} · analyzing…
+
+ ⏳ CodeBoarding is analyzing the architecture changes in this PR. This usually takes a few minutes.
+
+ codeboarding-action · run ${{ github.run_id }}
+ GITHUB_TOKEN: ${{ inputs.github_token }}
+
- name: Checkout CodeBoarding engine
if: steps.guard.outputs.skip != 'true'
uses: actions/checkout@v4
@@ -282,6 +297,10 @@ runs:
echo "Provider: $PROVIDER -> $PROVIDER_ENV; key length: ${#KEY}"
if [ "$PROVIDER" = "openrouter" ]; then
+ # Default models on OpenRouter when the user didn't pin one (cheap Gemini).
+ # Other providers fall through to the engine's own per-provider default.
+ AGENT_MODEL="${AGENT_MODEL:-google/gemini-3-flash-preview}"
+ PARSING_MODEL="${PARSING_MODEL:-google/gemini-3.1-flash-lite-preview}"
# OpenRouter-only checks. The litellm 'openrouter/...' model prefix 400s
# the engine's native OpenRouter call; other providers use native ids.
for M in "$AGENT_MODEL" "$PARSING_MODEL"; do
diff --git a/scripts/run_local.sh b/scripts/run_local.sh
index b575c02..34cb2b3 100755
--- a/scripts/run_local.sh
+++ b/scripts/run_local.sh
@@ -65,8 +65,8 @@ run_engine() {
PROJECT_ROOT="$ENGINE" \
DIAGRAM_DEPTH_LEVEL="$DEPTH" \
CACHING_DOCUMENTATION="false" \
- ENABLE_MONITORING="false" \
- OPENROUTER_API_KEY="${OPENROUTER_API_KEY:-}"
+ ENABLE_MONITORING="false"
+ # OPENROUTER_API_KEY is inherited from the environment (full mode requires it).
# Pass the model only when set; empty -> engine's own valid per-provider default.
if [ -n "$AGENT_MODEL" ]; then export AGENT_MODEL; fi
if [ -n "$PARSING_MODEL" ]; then export PARSING_MODEL; fi
@@ -78,8 +78,9 @@ if [ -n "$BASE_JSON" ] && [ -n "$HEAD_JSON" ]; then
BASE_ANALYSIS="$BASE_JSON"
HEAD_ANALYSIS="$HEAD_JSON"
else
- [ -n "$REPO" ] && [ -n "$BASE_REF" ] && [ -n "$HEAD_REF" ] || {
- echo "Need either --base-json/--head-json, or --repo/--base/--head." >&2; exit 2; }
+ if [ -z "$REPO" ] || [ -z "$BASE_REF" ] || [ -z "$HEAD_REF" ]; then
+ echo "Need either --base-json/--head-json, or --repo/--base/--head." >&2; exit 2
+ fi
[ -d "$ENGINE" ] || { echo "Engine not found at $ENGINE (set --engine or \$ENGINE)." >&2; exit 2; }
[ -n "${OPENROUTER_API_KEY:-}" ] || { echo "Export OPENROUTER_API_KEY for the full pipeline." >&2; exit 2; }
REPO="$(cd "$REPO" && pwd)"