From 41df91c90e64e2adf41f7692937f4041606624dd Mon Sep 17 00:00:00 2001 From: brovatten Date: Sat, 6 Jun 2026 16:49:58 +0200 Subject: [PATCH 1/6] docs: restyle README to match the CodeBoarding repo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Match the engine repo's README formatting: plain title + tagline + intro, a `·`-separated links line, language badges, sentence-case section headers, period-terminated bullets, lighter bold, and no emoji legend (drop the centered logo block). Content is unchanged — formatting only. --- README.md | 63 +++++++++++++++++++++++++++++++------------------------ 1 file changed, 36 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index e2cb55e..d48dc1c 100644 --- a/README.md +++ b/README.md @@ -1,16 +1,25 @@ -
- CodeBoarding Logo +# CodeBoarding Visual Architecture 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) + +[![JavaScript](https://img.shields.io/badge/JavaScript-222222?style=flat-square&logo=javascript&logoColor=F7DF1E)](https://developer.mozilla.org/en-US/docs/Web/JavaScript) +[![TypeScript](https://img.shields.io/badge/TypeScript-3178C6?style=flat-square&logo=typescript&logoColor=white)](https://www.typescriptlang.org/) +[![Java](https://img.shields.io/badge/Java-E76F00?style=flat-square&logo=openjdk&logoColor=white)](https://www.java.com/) +[![Python](https://img.shields.io/badge/Python-3776AB?style=flat-square&logo=python&logoColor=white)](https://www.python.org/) +[![Go](https://img.shields.io/badge/Go-00ADD8?style=flat-square&logo=go&logoColor=white)](https://go.dev/) +[![PHP](https://img.shields.io/badge/PHP-777BB4?style=flat-square&logo=php&logoColor=white)](https://www.php.net/) +[![Rust](https://img.shields.io/badge/Rust-000000?style=flat-square&logo=rust&logoColor=white)](https://www.rust-lang.org/) +[![C#](https://custom-icon-badges.demolab.com/badge/C%23-512BD4.svg?style=flat-square&logo=cshrp&logoColor=white)](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: @@ -31,7 +40,7 @@ graph LR linkStyle 1 stroke:#1f883d,stroke-width:2px; ``` -## Usage +## Quick start Create `.github/workflows/codeboarding.yml`: @@ -40,7 +49,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 +79,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 (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 +96,11 @@ 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. +Model format on OpenRouter is a bare slug (e.g. `anthropic/claude-sonnet-4`): exactly one `/`, with no `openrouter/` prefix (that is the LiteLLM form, which the action rejects 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 +108,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 +125,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` | engine default | Analysis model. Bare OpenRouter slug (e.g. `anthropic/claude-sonnet-4`); empty uses the engine's per-provider default. | +| `parsing_model` | engine default | Parsing model. Bare OpenRouter slug; empty uses the engine's per-provider 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 +169,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: From 3690d787d187457541baa0dde5ddeaae611b61ee Mon Sep 17 00:00:00 2001 From: brovatten Date: Sat, 6 Jun 2026 17:02:48 +0200 Subject: [PATCH 2/6] docs: rename to 'CodeBoarding Review'; real level-1 architecture sample Title -> 'CodeBoarding Review'. Replace the toy gateway/cache sample with CodeBoarding's actual level-1 components (from .codeboarding/analysis.json), colored as a diff: 1 added (green), 2 modified (yellow), 1 deleted (red dashed), with edges colored to match. --- README.md | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index d48dc1c..88062f9 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# CodeBoarding Visual Architecture Review +# CodeBoarding Review Review system design on every pull request, not just the diff. @@ -25,19 +25,30 @@ 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; ``` ## Quick start From 9c9f26f3a8174988ed580838358a5129dbe1451f Mon Sep 17 00:00:00 2001 From: brovatten Date: Sat, 6 Jun 2026 17:07:48 +0200 Subject: [PATCH 3/6] feat: default to Gemini on OpenRouter; drop model-format paragraph Default OpenRouter runs to google/gemini-3-flash-preview (agent) and google/gemini-3.1-flash-lite-preview (parsing) when the user doesn't pin a model. Gated on provider==openrouter, so other providers still defer to their own engine default (multi-provider intact). Both slugs verified to exist on OpenRouter. Remove the 'Model format on OpenRouter ...' README paragraph and update the Inputs table. --- README.md | 6 ++---- action.yml | 8 ++++++-- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 88062f9..236b96f 100644 --- a/README.md +++ b/README.md @@ -107,8 +107,6 @@ Models are optional. Omit `agent_model` and `parsing_model` to use the engine's parsing_model: google/gemini-3-flash-preview # optional ``` -Model format on OpenRouter is a bare slug (e.g. `anthropic/claude-sonnet-4`): exactly one `/`, with no `openrouter/` prefix (that is the LiteLLM form, which the action rejects 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: @@ -159,8 +157,8 @@ The command needs the `issue_comment` trigger and runs from your default branch | `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 uses the engine's per-provider default. | -| `parsing_model` | engine default | Parsing model. Bare OpenRouter slug; empty uses the 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. | diff --git a/action.yml b/action.yml index 24bcf5f..69dc20d 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: @@ -282,6 +282,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 From b47079fa1f25a97094179ce29baae9b07e68171a Mon Sep 17 00:00:00 2001 From: brovatten Date: Sat, 6 Jun 2026 17:12:56 +0200 Subject: [PATCH 4/6] docs: link GitHub's secrets guide from the Quick start --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 236b96f..5c627ba 100644 --- a/README.md +++ b/README.md @@ -90,7 +90,7 @@ 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-... From 50d7444f0cd4b6cc8a700388f6d24e4c5ae816e1 Mon Sep 17 00:00:00 2001 From: brovatten Date: Sat, 6 Jun 2026 17:30:55 +0200 Subject: [PATCH 5/6] feat(action): post an in-progress comment, edited in place when done MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add an early sticky-comment step (same 'codeboarding-architecture-diff' header) right after the guard, before the multi-minute checkout/analysis, so the PR shows an 'analyzing…' comment within seconds. The final post step edits that same comment in place with the diagram (and the failure step replaces it on error). best-effort (continue-on-error) so a placeholder hiccup can't fail the run. Consumers get the Qodo/CodeRabbit-style placeholder for free. --- action.yml | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/action.yml b/action.yml index 69dc20d..a142d58 100644 --- a/action.yml +++ b/action.yml @@ -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 From afbb9972e53fd090f73d8f63df85551183e0fea9 Mon Sep 17 00:00:00 2001 From: brovatten Date: Sat, 6 Jun 2026 18:16:42 +0200 Subject: [PATCH 6/6] fix(run_local): make shellcheck clean (CI lint was red) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The CI lint job (shellcheck) failed on three info-level findings in run_local.sh (pre-existing since the run_engine rewrite): SC2030/SC2031 from re-exporting OPENROUTER_API_KEY inside run_engine's subshell, and SC2015 from the A && B && C || {…} precondition. Drop the redundant key re-export (it's inherited from the env) and rewrite the precondition as an if. shellcheck now passes. --- scripts/run_local.sh | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) 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)"