Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
45 changes: 27 additions & 18 deletions .codeboarding/analysis.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"metadata": {
"generated_at": "2026-06-11T16:36:04.958976+00:00",
"commit_hash": "e9aa6435ce9f0ffe6726d5c1a3758980f79f1c3b",
"generated_at": "2026-06-11T16:41:16.710439+00:00",
"commit_hash": "ba5d7787eaecfa38964dd4663606927d98e501ba",
"repo_name": "CodeBoarding-action",
"depth_level": 1,
"file_coverage_summary": {
Expand All @@ -14,7 +14,7 @@
}
}
},
"description": "The CodeBoarding system orchestrates the analysis of git repositories, performs structural diffing between analysis states, and generates actionable visual feedback for pull requests by integrating with developer environments.",
"description": "The CodeBoarding-action architecture is a CI/CD pipeline that automates architectural observability by comparing codebase versions, generating visual Mermaid.js diffs, and providing actionable IDE deep-links within GitHub Pull Requests.",
"files": {
"scripts/cb_engine.py": {
"method_keys": [
Expand Down Expand Up @@ -377,8 +377,8 @@
},
"components": [
{
"name": "Engine Orchestrator",
"description": "Manages the execution environment and lifecycle of the CodeBoarding analysis engine. It handles the checkout of different git references, executes the analysis, and validates the integrity of the generated data before downstream processing.",
"name": "Analysis Orchestrator",
"description": "Manages the execution lifecycle of the GitHub Action, including environment setup, triggering analysis, and data validation.",
"key_entities": [
{
"qualified_name": "scripts.cb_engine.main",
Expand Down Expand Up @@ -429,8 +429,8 @@
"can_expand": true
},
{
"name": "Visual Diff Engine",
"description": "The core analytical component that compares two sets of analysis data. It identifies structural modifications (additions, removals, or changes in relationships), filters for relevance to reduce diagram noise, and renders the final architectural diff using Mermaid.js syntax.",
"name": "Visual Diff Generator",
"description": "The core logic engine that compares structural metadata between codebase versions and renders differences into Mermaid.js graph syntax.",
"key_entities": [
{
"qualified_name": "scripts.diff_to_mermaid.main",
Expand All @@ -451,10 +451,10 @@
"reference_end_line": 521
},
{
"qualified_name": "scripts.diff_to_mermaid._filter_changed",
"qualified_name": "scripts.diff_to_mermaid._diff_components",
"reference_file": "scripts/diff_to_mermaid.py",
"reference_start_line": 312,
"reference_end_line": 354
"reference_start_line": 162,
"reference_end_line": 207
}
],
"source_cluster_ids": [
Expand Down Expand Up @@ -503,8 +503,8 @@
"can_expand": true
},
{
"name": "Engagement & Feedback Manager",
"description": "Enhances the PR comment with interactive elements. It detects the developer's environment and generates deep-links to local IDEs or the CodeBoarding dashboard, transforming a static diagram into an actionable entry point for code review.",
"name": "UX & Integration Helper",
"description": "Manages the generation of user-facing outputs, including GitHub comments, status checks, and interactive links (CTAs) for external viewing. This component integrates the new 'build_cta.py' logic to generate interactive webview links and editor-specific deep links, expanding its role from simple reporting to providing actionable CI/CD report navigation. It receives processed data from the Visual Diff Generator to finalize the GitHub Action output.",
"key_entities": [
{
"qualified_name": "scripts.build_cta.main",
Expand Down Expand Up @@ -545,22 +545,31 @@
],
"components_relations": [
{
"relation": "Supplies validated JSON analysis files to",
"src_name": "Engine Orchestrator",
"dst_name": "Visual Diff Engine",
"relation": "Passes validated analysis artifacts to initiate structural comparison",
"src_name": "Analysis Orchestrator",
"dst_name": "Visual Diff Generator",
"src_id": "1",
"dst_id": "2",
"edge_count": 0,
"is_static": false
},
{
"relation": "Provides structural context and identified changes to",
"src_name": "Visual Diff Engine",
"dst_name": "Engagement & Feedback Manager",
"relation": "Provides context of changed files and components for deep-link generation",
"src_name": "Visual Diff Generator",
"dst_name": "UX & Integration Helper",
"src_id": "2",
"dst_id": "3",
"edge_count": 0,
"is_static": false
},
{
"relation": "Returns formatted markdown snippets and CTA links for final output",
"src_name": "UX & Integration Helper",
"dst_name": "Analysis Orchestrator",
"src_id": "3",
"dst_id": "1",
"edge_count": 0,
"is_static": false
}
]
}
28 changes: 5 additions & 23 deletions .codeboarding/health/health_report.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
{
"repository_name": "CodeBoarding-action",
"timestamp": "2026-06-11T16:35:41.927521+00:00",
"overall_score": 0.999609375,
"timestamp": "2026-06-11T16:41:11.294364+00:00",
"overall_score": 0.9996183206106869,
"check_summaries": [
{
"check_name": "function_size",
"description": "Checks that functions/methods do not exceed line count thresholds",
"check_type": "standard",
"total_entities_checked": 42,
"total_entities_checked": 43,
"findings_count": 0,
"warning_count": 0,
"score": 1.0,
Expand All @@ -17,7 +17,7 @@
"check_name": "fan_out",
"description": "Checks efferent coupling: how many other functions each function calls",
"check_type": "standard",
"total_entities_checked": 42,
"total_entities_checked": 43,
"findings_count": 0,
"warning_count": 0,
"score": 1.0,
Expand All @@ -27,7 +27,7 @@
"check_name": "fan_in",
"description": "Checks afferent coupling: how many other functions call each function",
"check_type": "standard",
"total_entities_checked": 42,
"total_entities_checked": 43,
"findings_count": 0,
"warning_count": 0,
"score": 1.0,
Expand All @@ -43,24 +43,6 @@
"score": 1.0,
"finding_groups": []
},
{
"check_name": "circular_dependencies",
"description": "Detects circular dependencies between packages",
"check_type": "circular_dependencies",
"cycles": [],
"packages_checked": 1,
"packages_in_cycles": 0
},
{
"check_name": "package_instability",
"description": "Computes Martin's instability metric (I = Ce / (Ca + Ce)) per package",
"check_type": "standard",
"total_entities_checked": 0,
"findings_count": 0,
"warning_count": 0,
"score": 1.0,
"finding_groups": []
},
{
"check_name": "unused_code_diagnostics",
"description": "Detects unused imports, variables, functions, and dead code via LSP diagnostics",
Expand Down
4 changes: 3 additions & 1 deletion .github/workflows/codeboarding.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ on:
types: [created]

permissions:
contents: read
# write: the action commits the generated .codeboarding/analysis.json back to the
# PR branch so the webview can open this PR's diff at the head SHA (same-repo PRs).
contents: write
pull-requests: write
issues: write

Expand Down
7 changes: 6 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,9 @@ on:
types: [created]

permissions:
contents: read
# write lets the action commit analysis.json to the PR branch so the comment can
# link to the webview diff. Drop to `read` to keep the comment without that link.
contents: write
pull-requests: write
issues: write

Expand Down Expand Up @@ -156,6 +158,7 @@ The command needs the `issue_comment` trigger and runs from your default branch
| `llm_api_key` | required | Your LLM provider API key (see `llm_provider`). |
| `llm_provider` | `openrouter` | Provider for the key, mapped to `<NAME>_API_KEY` (e.g. `anthropic`, `openai`, `google`). |
| `github_token` | `${{ github.token }}` | Token used to post or update the PR comment. |
| `push_token` | `${{ github.token }}` | Token used to push the generated `analysis.json` to the PR branch (for the webview link). The workflow token can push when the workflow grants `permissions: contents: write`. Separate from `github_token` so commenting can use a GitHub App token while the push uses the workflow token. |
| `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. |
Expand All @@ -166,6 +169,8 @@ The command needs the `issue_comment` trigger and runs from your default branch
| `comment_header` | `Architecture review` | Heading for the PR comment. |
| `trigger_command` | `/codeboarding` | Slash command for trusted on-demand runs. |
| `cta_base_url` | empty | Click-proxy base URL: deep-links the editor link into VS Code/Cursor and adds a "get the extension" link (tracks owner/repo/pr). Empty links to the extension listing instead (GitHub strips `vscode:`/`cursor:` from comments). |
| `webview_base_url` | `https://app.codeboarding.org` | Hosted webview base URL. The PR comment adds an "explore in browser" link to this PR's head-vs-base diff. Needs `commit_head_analysis` (same-repo PRs only); omitted on forks. Set empty to disable. |
| `commit_head_analysis` | `true` | Commit the generated head `.codeboarding/analysis.json` (+ health report) to the PR branch so the webview can read it at the head SHA. Same-repo PRs only (the token is read-only on forks). |

## Outputs

Expand Down
95 changes: 93 additions & 2 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ inputs:
description: 'GITHUB_TOKEN used to post the PR comment. Defaults to the workflow token.'
required: false
default: ${{ github.token }}
push_token:
description: 'Token used to push the generated .codeboarding/analysis.json to the PR branch (for the webview link). Defaults to the workflow github.token, which can push when the calling workflow grants "permissions: contents: write". Kept separate from github_token so commenting can use a GitHub App token while the push uses the workflow token (whose write access the consumer controls).'
required: false
default: ${{ github.token }}
engine_ref:
description: 'Git ref (tag/branch/SHA) of CodeBoarding/CodeBoarding used as the analysis engine. Pinned to a release for reproducibility; override to track a newer ref.'
required: false
Expand Down Expand Up @@ -54,6 +58,14 @@ inputs:
description: 'Base URL of the click proxy (e.g. https://go.codeboarding.org). When set, the editor link deep-links into VS Code/Cursor via the proxy and a "get the extension" link is added (owner/repo/pr tracked). Empty (default) links to the extension listing instead, since GitHub strips vscode:/cursor: schemes from comment links.'
required: false
default: ''
webview_base_url:
description: 'Base URL of the hosted webview (default https://app.codeboarding.org). The PR comment adds an "explore in browser" link deep-linking to this PR''s head-vs-base architecture diff. Requires the head analysis.json to be committed to the PR branch (commit_head_analysis), so it is omitted on fork PRs. Set empty to disable the webview link.'
required: false
default: 'https://app.codeboarding.org'
commit_head_analysis:
description: 'Commit the generated head .codeboarding/analysis.json (+ health report) back to the PR branch so the webview can fetch it at the head SHA. Same-repo PRs only (the token is read-only on forks). Required for the webview "explore in browser" link.'
required: false
default: 'true'
trigger_command:
description: 'Slash-command that triggers the action from a PR comment (issue_comment event). A comment whose first word is this runs the diagram on-demand.'
required: false
Expand Down Expand Up @@ -88,6 +100,7 @@ runs:
PR_NUMBER_PULL: ${{ github.event.pull_request.number }}
PULL_BASE_SHA: ${{ github.event.pull_request.base.sha }}
PULL_HEAD_SHA: ${{ github.event.pull_request.head.sha }}
PULL_HEAD_REF: ${{ github.event.pull_request.head.ref }}
PULL_BASE_REF: ${{ github.event.pull_request.base.ref }}
PULL_BASE_REPO: ${{ github.event.pull_request.base.repo.full_name }}
PULL_HEAD_REPO: ${{ github.event.pull_request.head.repo.full_name }}
Expand Down Expand Up @@ -115,6 +128,7 @@ runs:
PR_NUMBER="$PR_NUMBER_PULL"
BASE_SHA="$PULL_BASE_SHA"
HEAD_SHA="$PULL_HEAD_SHA"
HEAD_REF="$PULL_HEAD_REF"
BASE_REF="$PULL_BASE_REF"
BASE_REPO="$PULL_BASE_REPO"
HEAD_REPO="$PULL_HEAD_REPO"
Expand All @@ -137,6 +151,7 @@ runs:
PR_JSON="$(gh api "repos/${REPOSITORY}/pulls/${PR_NUMBER}" 2>/dev/null)" || skip "Could not fetch PR #$PR_NUMBER from the API."
BASE_SHA="$(printf '%s' "$PR_JSON" | python3 -c 'import json,sys;print(json.load(sys.stdin)["base"]["sha"])' 2>/dev/null)" || skip "Could not parse base SHA from the PR API."
HEAD_SHA="$(printf '%s' "$PR_JSON" | python3 -c 'import json,sys;print(json.load(sys.stdin)["head"]["sha"])' 2>/dev/null)" || skip "Could not parse head SHA from the PR API."
HEAD_REF="$(printf '%s' "$PR_JSON" | python3 -c 'import json,sys;print(json.load(sys.stdin)["head"]["ref"])' 2>/dev/null)" || HEAD_REF=""
BASE_REF="$(printf '%s' "$PR_JSON" | python3 -c 'import json,sys;print(json.load(sys.stdin)["base"]["ref"])' 2>/dev/null)" || BASE_REF=""
BASE_REPO="$(printf '%s' "$PR_JSON" | python3 -c 'import json,sys;print(json.load(sys.stdin)["base"]["repo"]["full_name"])' 2>/dev/null)" || skip "Could not parse base repo from the PR API."
HEAD_REPO="$(printf '%s' "$PR_JSON" | python3 -c 'import json,sys;print(json.load(sys.stdin)["head"]["repo"]["full_name"])' 2>/dev/null)" || skip "Could not parse head repo from the PR API."
Expand All @@ -151,9 +166,12 @@ runs:
echo "pr_number=$PR_NUMBER"
echo "base_sha=$BASE_SHA"
echo "head_sha=$HEAD_SHA"
echo "head_ref=$HEAD_REF"
echo "base_ref=$BASE_REF"
echo "base_repo=$BASE_REPO"
echo "head_repo=$HEAD_REPO"
# same_repo gates pushing the head analysis: forks give a read-only token.
if [ "$HEAD_REPO" = "$REPOSITORY" ]; then echo "same_repo=true"; else echo "same_repo=false"; fi
} >> "$GITHUB_OUTPUT"
echo "Resolved PR #$PR_NUMBER (base=$BASE_REPO@$BASE_SHA head=$HEAD_REPO@$HEAD_SHA) via $EVENT"

Expand Down Expand Up @@ -576,6 +594,67 @@ runs:
"${RUNNER_TEMP}/cb-agent-model" \
"${RUNNER_TEMP}/cb-parsing-model"

# Commit the generated head analysis (+ health report) to the PR branch so the
# hosted webview can fetch .codeboarding/analysis.json at the head SHA and open
# this PR's head-vs-base diff. Same-repo PRs only — the token is read-only on
# forks (the step is skipped there and the webview link is omitted). The push
# creates a NEW head commit; its SHA (webview_sha) is what the comment links to.
- name: Commit head analysis to PR branch
id: commit_head
if: >-
steps.guard.outputs.skip != 'true'
&& inputs.commit_head_analysis == 'true'
&& steps.guard.outputs.same_repo == 'true'
&& inputs.webview_base_url != ''
shell: bash
working-directory: target-repo
env:
# Push with push_token (defaults to the workflow github.token, gated by the
# consumer's `permissions: contents: write`) — NOT github_token, which may be
# a GitHub App token used only for commenting and need not have write access.
GH_TOKEN: ${{ inputs.push_token }}
HEAD_DIR: ${{ steps.base.outputs.head_dir }}
HEAD_REF: ${{ steps.guard.outputs.head_ref }}
HEAD_SHA: ${{ steps.guard.outputs.head_sha }}
REPOSITORY: ${{ github.repository }}
SERVER_URL: ${{ github.server_url }}
run: |
echo "ready=false" >> "$GITHUB_OUTPUT"
echo "webview_sha=$HEAD_SHA" >> "$GITHUB_OUTPUT"
[ -n "$HEAD_REF" ] || { echo "::notice::No head branch ref resolved; skipping head-analysis commit."; exit 0; }
[ -f "$HEAD_DIR/analysis.json" ] || { echo "::notice::No head analysis.json to commit."; exit 0; }

mkdir -p .codeboarding/health
cp "$HEAD_DIR/analysis.json" .codeboarding/analysis.json
if [ -f "$HEAD_DIR/health/health_report.json" ]; then
cp "$HEAD_DIR/health/health_report.json" .codeboarding/health/health_report.json
fi

git add .codeboarding/analysis.json .codeboarding/health/health_report.json 2>/dev/null || git add .codeboarding/analysis.json
if git diff --cached --quiet; then
echo "::notice::Head analysis unchanged; nothing to commit."
echo "ready=true" >> "$GITHUB_OUTPUT" # already committed at this SHA → webview can read it
exit 0
fi

git config user.name "codeboarding[bot]"
git config user.email "codeboarding[bot]@users.noreply.github.com"
git commit -m "chore(codeboarding): update architecture analysis [skip ci]" >/dev/null

# Push to the PR head branch. The checkout used persist-credentials:false, so
# authenticate the push explicitly with the workflow token (same-repo only).
# Requires `contents: write` on the job's token — a read-only token (the
# default) is rejected here; the push error below names the cause.
AUTH_URL="https://x-access-token:${GH_TOKEN}@${SERVER_URL#https://}/${REPOSITORY}.git"
if git push "$AUTH_URL" "HEAD:refs/heads/${HEAD_REF}"; then
NEW_SHA="$(git rev-parse HEAD)"
echo "webview_sha=$NEW_SHA" >> "$GITHUB_OUTPUT"
echo "ready=true" >> "$GITHUB_OUTPUT"
echo "Committed head analysis to ${HEAD_REF} as ${NEW_SHA}."
else
echo "::warning::Could not push head analysis to ${HEAD_REF}; the webview link will be omitted. Most likely the job's token lacks 'contents: write' (add 'permissions: contents: write' to the calling workflow), or the branch is protected against this pusher."
fi

- name: Diff analyses → Mermaid
if: steps.guard.outputs.skip != 'true'
id: diagram
Expand Down Expand Up @@ -634,6 +713,12 @@ runs:
TRUNC: ${{ steps.diagram.outputs.truncated }}
PR: ${{ steps.guard.outputs.pr_number }}
ISSUES: ${{ steps.health.outputs.issues }}
WEBVIEW_BASE: ${{ inputs.webview_base_url }}
# SHA the head analysis.json was committed at (post-push), and whether that
# commit succeeded — gates the webview "explore in browser" link.
WEBVIEW_SHA: ${{ steps.commit_head.outputs.webview_sha }}
WEBVIEW_READY: ${{ steps.commit_head.outputs.ready }}
BASE_SHA: ${{ steps.guard.outputs.base_sha }}
run: |
BODY_FILE=$(mktemp)
OWNER="${OWNER_REPO%%/*}"; REPO="${OWNER_REPO##*/}"
Expand All @@ -646,11 +731,17 @@ runs:
}

# CTA footer: an editor link (proxy deep-link when CTA_BASE is set, else the
# extension's https listing — GitHub strips vscode:/cursor:) plus the ⚠️ banner.
# extension's https listing — GitHub strips vscode:/cursor:), the ⚠️ banner,
# and — when the head analysis was committed (WEBVIEW_READY) — a webview
# "explore in browser" link to this PR's head-vs-base diff.
cta() {
local extra=()
if [ "$WEBVIEW_READY" = "true" ]; then
extra+=(--webview-ready --webview-base "$WEBVIEW_BASE" --head-sha "$WEBVIEW_SHA" --base-sha "$BASE_SHA")
fi
python3 "$ACTION_PATH/scripts/build_cta.py" \
--cta-base "$CTA_BASE" --owner "$OWNER" --repo "$REPO" --pr "$PR" \
--repo-path "$TARGET_REPO" --issues "${ISSUES:-0}"
--repo-path "$TARGET_REPO" --issues "${ISSUES:-0}" "${extra[@]}"
}

{
Expand Down
Loading