Add codeflash org CI audit case study and interactive Dash report

Case study in .codeflash/krrt7/codeflash-ai/ci-audit/ with README,
status, and raw data (fork activity, PRs merged).

Interactive Dash report in reports/codeflash-ci-audit/ with two tabs:
Executive Summary (hero metrics, cost impact charts, before/after) and
Full Detail (fork breakdown, findings table, PR inventory, methodology).

Key numbers: 71% fewer workflow runs, ~$12K/yr in Enterprise overage
savings, 200+ forks disabled, 11 PRs merged across 2 repos.
This commit is contained in:
Kevin Turcios 2026-04-23 03:56:04 -05:00
parent 8221ce32a2
commit c492164fbf
7 changed files with 1259 additions and 0 deletions

View file

@ -0,0 +1,112 @@
# Codeflash Org CI Audit
Comprehensive CI/CD audit of the `codeflash-ai` GitHub org: 200+ forks, 2 main repos (`codeflash`, `codeflash-internal`).
## Background
While working on Unstructured optimization case studies, discovered that codeflash-ai org forks were running GitHub Actions unnecessarily — Dependabot updates, upstream scheduled CI, and failing workflows creating ~960 noise runs/month across 26 active forks out of 200+.
Expanded scope to a full audit of CI hygiene across both main repos.
## Results
### Fork CI
| Metric | Before | After |
|---|---|---|
| Forks with active CI | 26 of 200+ | 0 |
| Wasted runs/month | ~960 | 0 |
| Estimated cost/yr (private rates) | $248 | $0 |
Top offenders before disable:
| Repo | Runs (audit period) | Cost/yr | Pattern |
|---|---|---|---|
| sglang | 4,936 | $4 | CI Failure Monitor (cron, skips on fork) |
| kornia | 308 | $226 | Daily macOS + Windows test matrix (91% of all fork cost) |
| ray | 527 | $3 | Dependabot + Stale PR bot |
| lerobot | 330 | $12 | Dependabot + Full Tests |
| next.js | 217 | $3 | Dependabot only |
| 21 others | 694 | <$5 | Dependabot / misc |
Actions disabled on all 200+ forks on 2026-04-23.
### Main Repo — codeflash
| Finding | Impact | Fix |
|---|---|---|
| Wildcard path triggers (`paths: ['**']`) on 12 E2E workflows | Every PR (including README edits) triggered ~2hrs of E2E tests | Targeted path filters (#2025) |
| Broken claude-code-action (v1.0.90 Bedrock OIDC regression) | 60-100% failure rate on Claude Code workflows | Pinned to v1.0.89 (#2026) |
| Dependabot scanning test fixtures (no `dependabot.yml`) | 70% of Dependabot runs failing on `code_to_optimize/` fixtures | Added explicit config excluding fixtures (#2027) |
| 13 ghost workflows (deleted source files, still active in UI) | Actions dashboard clutter, confusing status signals | `gh workflow disable` on all 13 |
| 17 separate workflow files with individual required checks | Workflow-only PRs stuck at "Pending", required admin merge | Consolidated into single `ci.yaml` with gate job (#2044) |
| Stale `codeflash.yaml` | Superseded by `codeflash-optimize.yaml`, still active | Deleted (#2056) |
| No npm/Maven caching, duplicate workflows | Slow JS/Java CI, redundant compute | Consolidated + caching (#2050) |
| Broken Windows paths, outdated action versions | CI failures on Windows matrix | Fixed paths + upgraded actions (#2052, #2053) |
| Flaky E2E tests | False failures blocking merges | Increased test data size + timeout (#2051, #2057) |
### Main Repo — codeflash-internal
| Finding | Impact | Fix |
|---|---|---|
| Deploy AI Service path filter included `.github/workflows/**` | Any workflow edit triggered a production deploy | Scoped to actual service paths (#2588) |
| Claude Code workflow had no path filters | Fired on every PR/comment regardless of files changed | Added `paths-ignore` (#2588) |
| Publish to PyPI permanently disabled (`if: false`) | Created skipped run noise on every push to main | Disabled via API + workflow fix (#2588) |
| Broken claude-code-action (same v1.0.90 regression) | 85% failure rate | Pinned to v1.0.89 (#2587) |
### Operational Improvements
| Before | After |
|---|---|
| 17 individual workflow files, 13 required checks in branch protection | Single `ci.yaml`, 1 gate check (`required-checks-passed`) |
| Workflow-only PRs stuck at "Pending", required admin merge | Skipped jobs report as "skipped", gate accepts them, self-service merge |
| Branch protection rules | Repository rulesets (modern GitHub standard) |
| ~$1.85 compute burned on every non-code PR | ~$0.001 (change detection only) |
| 22 workflow files in repo | 7 workflow files |
## PRs Merged
### codeflash
| PR | Date | Description |
|---|---|---|
| [#2025](https://github.com/codeflash-ai/codeflash/pull/2025) | Apr 9 | Replace wildcard path triggers on 12 E2E workflows |
| [#2026](https://github.com/codeflash-ai/codeflash/pull/2026) | Apr 9 | Pin claude-code-action to v1.0.89 (Bedrock auth fix) |
| [#2027](https://github.com/codeflash-ai/codeflash/pull/2027) | Apr 9 | Exclude test fixtures from Dependabot |
| [#2044](https://github.com/codeflash-ai/codeflash/pull/2044) | Apr 9 | Consolidate 17 workflows into single ci.yaml with gate job |
| [#2047](https://github.com/codeflash-ai/codeflash/pull/2047) | Apr 9 | Path filters, validate-pr action, fetch-depth, continue-on-error |
| [#2050](https://github.com/codeflash-ai/codeflash/pull/2050) | Apr 9 | npm cache, Maven consolidation, remove duplicate workflow |
| [#2052](https://github.com/codeflash-ai/codeflash/pull/2052) | Apr 10 | Upgrade action versions, add uv cache, fix broken paths, DRY publish |
| [#2053](https://github.com/codeflash-ai/codeflash/pull/2053) | Apr 10 | Fix shell: bash for Windows conditional install |
| [#2056](https://github.com/codeflash-ai/codeflash/pull/2056) | Apr 10 | Delete disabled codeflash.yaml workflow |
### codeflash-internal
| PR | Date | Description |
|---|---|---|
| [#2587](https://github.com/codeflash-ai/codeflash-internal/pull/2587) | Apr 9 | Pin claude-code-action to v1.0.89 |
| [#2588](https://github.com/codeflash-ai/codeflash-internal/pull/2588) | Apr 9 | Fix deploy path filter, Claude Code paths, disable PyPI |
### Direct Actions
| Action | Date | Details |
|---|---|---|
| Disabled 13 ghost workflows | Apr 9 | `gh workflow disable` on codeflash |
| Disabled Publish to PyPI workflow | Apr 9 | `gh workflow disable` on codeflash-internal |
| Closed stale Dependabot PR #2012 | Apr 9 | vite bump to test fixture |
| Migrated branch protection to rulesets | Apr 9 | Modern GitHub standard, single gate check |
| Disabled Actions on 200+ forks | Apr 23 | `gh api --method PUT` on all org forks |
## Methodology
1. **Inventory**`gh repo list codeflash-ai` to enumerate all 200+ repos, classify as fork vs primary
2. **Fork scan** — Query Actions run counts per fork since Apr 2025, identify 26 active forks
3. **Compute cost** — Sample job-level data (duration, runner type), calculate at GitHub rates ($0.008/min Linux, $0.016/min Windows, $0.08/min macOS)
4. **Main repo audit** — List all workflows, check run history, failure rates, ghost detection, trigger config
5. **Root cause analysis** — Compare working vs broken runs by commit SHA + timestamp to pinpoint regressions
## Monitoring
- **anthropics/claude-code-action#1196** — unpin from v1.0.89 once Bedrock OIDC fix lands upstream
- **Dependabot alerts** — 24 known vulnerabilities (1 critical, 8 high) at time of audit; new `dependabot.yml` targets real deps only
- **Fork re-enable** — if a fork is needed for active development: `echo '{"enabled":true}' | gh api --method PUT repos/codeflash-ai/<repo>/actions/permissions --input -`

View file

@ -0,0 +1,13 @@
repo runs_audit_period cost_yr_usd runner_types pattern
sglang 4936 4 Linux CI Failure Monitor (cron, skips on fork)
ray 527 3 Linux Dependabot + Stale PR bot
lerobot 330 12 Linux Dependabot + Full Tests
kornia 308 226 Linux + Windows + macOS Daily scheduled CPU test matrix
next.js 217 3 Linux Dependabot only
strapi 118 1 Linux Dependabot (lodash across 20+ packages)
rasa 85 1 Linux Dependabot
Rocket.Chat 80 1 Linux Dependabot
nest 73 1 Linux Dependabot (@nestjs/core across 20+ samples)
openclaw 68 1 Linux Dependabot
appsmith 64 1 Linux Dependabot
other_15 206 1 Linux Dependabot / misc
1 repo runs_audit_period cost_yr_usd runner_types pattern
2 sglang 4936 4 Linux CI Failure Monitor (cron, skips on fork)
3 ray 527 3 Linux Dependabot + Stale PR bot
4 lerobot 330 12 Linux Dependabot + Full Tests
5 kornia 308 226 Linux + Windows + macOS Daily scheduled CPU test matrix
6 next.js 217 3 Linux Dependabot only
7 strapi 118 1 Linux Dependabot (lodash across 20+ packages)
8 rasa 85 1 Linux Dependabot
9 Rocket.Chat 80 1 Linux Dependabot
10 nest 73 1 Linux Dependabot (@nestjs/core across 20+ samples)
11 openclaw 68 1 Linux Dependabot
12 appsmith 64 1 Linux Dependabot
13 other_15 206 1 Linux Dependabot / misc

View file

@ -0,0 +1,12 @@
pr repo date title
2025 codeflash 2026-04-09 ci: replace wildcard path triggers on E2E tests
2026 codeflash 2026-04-09 ci: pin claude-code-action to v1.0.89 (fix Bedrock auth)
2027 codeflash 2026-04-09 ci: exclude test fixtures from Dependabot
2044 codeflash 2026-04-09 ci: consolidate required checks into single gate workflow
2047 codeflash 2026-04-09 ci: consolidate improvements — paths, validate-pr action, fetch-depth, continue-on-error
2050 codeflash 2026-04-09 Optimize CI: npm cache, Maven consolidation, remove duplicate workflow
2052 codeflash 2026-04-10 ci: upgrade action versions, add uv cache, fix broken paths, DRY publish
2053 codeflash 2026-04-10 fix(ci): add shell: bash to conditional install step for Windows
2056 codeflash 2026-04-10 chore: delete disabled codeflash.yaml workflow
2587 codeflash-internal 2026-04-09 ci: pin claude-code-action to v1.0.89
2588 codeflash-internal 2026-04-09 ci: fix deploy path filter, Claude Code paths, disable PyPI
1 pr repo date title
2 2025 codeflash 2026-04-09 ci: replace wildcard path triggers on E2E tests
3 2026 codeflash 2026-04-09 ci: pin claude-code-action to v1.0.89 (fix Bedrock auth)
4 2027 codeflash 2026-04-09 ci: exclude test fixtures from Dependabot
5 2044 codeflash 2026-04-09 ci: consolidate required checks into single gate workflow
6 2047 codeflash 2026-04-09 ci: consolidate improvements — paths, validate-pr action, fetch-depth, continue-on-error
7 2050 codeflash 2026-04-09 Optimize CI: npm cache, Maven consolidation, remove duplicate workflow
8 2052 codeflash 2026-04-10 ci: upgrade action versions, add uv cache, fix broken paths, DRY publish
9 2053 codeflash 2026-04-10 fix(ci): add shell: bash to conditional install step for Windows
10 2056 codeflash 2026-04-10 chore: delete disabled codeflash.yaml workflow
11 2587 codeflash-internal 2026-04-09 ci: pin claude-code-action to v1.0.89
12 2588 codeflash-internal 2026-04-09 ci: fix deploy path filter, Claude Code paths, disable PyPI

View file

@ -0,0 +1,27 @@
# ci-audit Status
Last updated: 2026-04-23
## Current state
Complete. All audit findings addressed, all forks disabled, all PRs merged.
## What was completed
### Session 1 (Apr 9)
- Full CI audit of codeflash-ai org (93 forks at the time + 2 main repos)
- 5 PRs merged: path triggers (#2025), claude-code-action pin (#2026, #2587), Dependabot config (#2027), internal fixes (#2588)
- 13 ghost workflows disabled
- CI gate workflow consolidated (#2044)
- Additional CI improvements (#2047, #2050, #2052, #2053, #2056)
- Branch protection migrated to rulesets
- Audit report written to `~/Desktop/work/cf_org/codeflash-ci-audit/`
### Session 2 (Apr 23)
- Disabled GitHub Actions on all 200+ org forks (previously blocked by missing org admin)
- Created case study in `.codeflash/krrt7/codeflash-ai/ci-audit/`
## Remaining / monitoring
- Unpin claude-code-action once anthropics/claude-code-action#1196 is fixed upstream
- Re-enable Actions on specific forks if needed for active optimization work

View file

@ -0,0 +1,919 @@
"""Codeflash Org — CI Audit Report
Two-tab report served at http://localhost:8051/:
1. Executive Summary hero metrics, key findings, before/after
2. Full Detail per-PR inventory, fork breakdown, methodology
"""
import json
import os
from pathlib import Path
import plotly.graph_objects as go
from dash import Dash, Input, Output, clientside_callback, dash_table, dcc, html
from theme import (
ACCENT,
AMBER,
BG,
BLUE,
CARD,
CARD_BG,
CARD_BORDER,
DARK,
FONT,
GRAY,
GREEN,
GRID_OVERLAY,
LIGHT_GRAY,
LIGHT_GREEN,
LIGHT_RED,
MONO,
PURPLE,
RED,
SLATE,
TABLE_CELL,
TABLE_DATA,
TABLE_DATA_CONDITIONAL,
TABLE_HEADER,
TABLE_WRAP,
WHITE,
)
# ── Data ────────────────────────────────────────────────────────────────────
_DATA = json.loads((Path(__file__).parent / "data.json").read_text())
CODEFLASH_BASE = _DATA["codeflash_base"]
INTERNAL_BASE = _DATA["internal_base"]
FORK_CI = _DATA["fork_ci"]
FINDINGS = _DATA["findings"]
PRS_MERGED = _DATA["prs_merged"]
DIRECT_ACTIONS = _DATA["direct_actions"]
OPS = _DATA["operational_before_after"]
RUN_VOL = _DATA["run_volume"]
BILLING = _DATA["billing"]
REPO_BASES = {
"codeflash": CODEFLASH_BASE,
"codeflash-internal": INTERNAL_BASE,
}
# ── Helpers ──────────────────────────────────────────────────────────────────
def hero_metric(value, label, detail, color=GREEN):
return html.Div(
[
html.Div(
value,
style={
"fontSize": "42px",
"fontWeight": "800",
"color": color,
"lineHeight": "1",
"letterSpacing": "-0.02em",
"fontFamily": FONT,
},
),
html.Div(
label,
style={
"fontSize": "15px",
"fontWeight": "600",
"color": SLATE,
"marginTop": "8px",
},
),
html.Div(
detail,
style={"fontSize": "13px", "color": GRAY, "marginTop": "4px"},
),
],
style={
"background": CARD_BG,
"borderRadius": "16px",
"padding": "32px 24px",
"textAlign": "center",
"flex": "1 1 0%",
"minWidth": "0",
"border": f"1px solid {CARD_BORDER}",
},
)
def section(title, subtitle=None):
children = [
html.H2(
title,
style={
"fontSize": "22px",
"fontWeight": "700",
"color": SLATE,
"margin": "0",
"fontFamily": FONT,
"letterSpacing": "-0.01em",
},
)
]
if subtitle:
children.append(
html.P(
subtitle,
style={
"fontSize": "14px",
"color": GRAY,
"margin": "6px 0 0",
"lineHeight": "1.5",
},
)
)
return html.Div(children, style={"margin": "56px 0 24px"})
def card(children, **kw):
style = {**CARD}
for k, v in kw.items():
style[k] = v
return html.Div(children, style=style)
def _logo_lockup(bolt_size="20px", text_size="24px", gap="12px", offset="4px"):
return html.Div(
[
html.Span(
"",
style={
"fontSize": bolt_size,
"position": "relative",
"top": offset,
},
),
html.Span(
"codeflash",
style={
"fontSize": text_size,
"fontWeight": "800",
"color": ACCENT,
"fontFamily": FONT,
"letterSpacing": "-0.03em",
},
),
],
style={"display": "inline-flex", "alignItems": "center", "gap": gap},
)
def _badge(text, color):
return html.Span(
text,
style={
"padding": "2px 10px",
"borderRadius": "999px",
"fontSize": "11px",
"fontWeight": "700",
"background": color,
"color": DARK,
},
)
# ── Charts ───────────────────────────────────────────────────────────────────
def make_fork_chart():
"""Horizontal bar: fork CI runs by repo."""
repos = [f["repo"] for f in FORK_CI[:8]]
runs = [f["runs"] for f in FORK_CI[:8]]
repos.reverse()
runs.reverse()
fig = go.Figure()
fig.add_trace(
go.Bar(
y=repos,
x=runs,
orientation="h",
marker_color=[ACCENT if r > 500 else BLUE for r in runs],
marker_cornerradius=4,
text=[f"{r:,}" for r in runs],
textposition="outside",
textfont={"size": 12, "color": SLATE},
)
)
fig.update_layout(
plot_bgcolor="rgba(0,0,0,0)",
paper_bgcolor="rgba(0,0,0,0)",
font={"family": FONT, "size": 13, "color": SLATE},
xaxis={"title": "Workflow Runs (audit period)", "gridcolor": CARD_BORDER, "zeroline": False},
yaxis={"title": "", "automargin": True},
margin={"t": 10, "b": 50, "l": 10, "r": 60},
height=320,
showlegend=False,
)
return fig
def make_fork_cost_chart():
"""Pie chart: fork CI cost breakdown."""
labels = [f["repo"] for f in FORK_CI if f["cost_yr"] >= 3]
values = [f["cost_yr"] for f in FORK_CI if f["cost_yr"] >= 3]
other = sum(f["cost_yr"] for f in FORK_CI if f["cost_yr"] < 3)
if other > 0:
labels.append("All others")
values.append(other)
colors = [ACCENT, BLUE, GREEN, PURPLE, AMBER, GRAY, LIGHT_GRAY, RED][:len(labels)]
fig = go.Figure()
fig.add_trace(
go.Pie(
labels=labels,
values=values,
hole=0.55,
marker={"colors": colors},
textinfo="label+percent",
textfont={"size": 12, "color": WHITE},
hovertemplate="%{label}: $%{value}/yr<extra></extra>",
)
)
fig.update_layout(
plot_bgcolor="rgba(0,0,0,0)",
paper_bgcolor="rgba(0,0,0,0)",
font={"family": FONT, "size": 13, "color": SLATE},
margin={"t": 10, "b": 10, "l": 10, "r": 10},
height=300,
showlegend=False,
annotations=[
{
"text": f"${sum(values)}/yr",
"x": 0.5,
"y": 0.5,
"font": {"size": 18, "color": ACCENT, "family": MONO},
"showarrow": False,
}
],
)
return fig
def make_before_after_chart():
"""Grouped bar: operational metrics before vs after."""
cats = ["Workflow Files", "Required Checks", "Failing Fork Runs/mo"]
before = [OPS["workflow_files"][0], OPS["required_checks"][0], OPS["fork_failing_runs_monthly"][0]]
after = [OPS["workflow_files"][1], OPS["required_checks"][1], OPS["fork_failing_runs_monthly"][1]]
fig = go.Figure()
fig.add_trace(
go.Bar(
name="Before",
x=cats,
y=before,
marker_color=LIGHT_GRAY,
marker_cornerradius=6,
text=[str(v) for v in before],
textposition="outside",
textfont={"size": 13, "color": GRAY},
)
)
fig.add_trace(
go.Bar(
name="After",
x=cats,
y=after,
marker_color=GREEN,
marker_cornerradius=6,
text=[str(v) for v in after],
textposition="outside",
textfont={"size": 13, "color": GREEN},
)
)
fig.update_layout(
barmode="group",
bargap=0.3,
bargroupgap=0.1,
plot_bgcolor="rgba(0,0,0,0)",
paper_bgcolor="rgba(0,0,0,0)",
font={"family": FONT, "size": 13, "color": SLATE},
yaxis={"gridcolor": CARD_BORDER, "zeroline": False},
xaxis={"title": ""},
margin={"t": 20, "b": 60, "l": 50, "r": 20},
legend={
"orientation": "h",
"yanchor": "bottom",
"y": 1.05,
"xanchor": "center",
"x": 0.5,
"font": {"size": 13},
},
height=360,
)
return fig
def make_run_volume_chart():
"""Bar chart: monthly workflow runs before vs after."""
months = ["Dec '25", "Jan '26", "Feb '26", "Mar '26", "Apr '26\n(projected)"]
runs = [4150, 9391, 21307, 14753, RUN_VOL["codeflash_apr_projected"]]
fig = go.Figure()
fig.add_trace(
go.Bar(
x=months,
y=runs,
marker_color=[LIGHT_GRAY, LIGHT_GRAY, RED, AMBER, GREEN],
marker_cornerradius=6,
text=[f"{r:,}" for r in runs],
textposition="outside",
textfont={"size": 13, "color": SLATE},
)
)
fig.add_hline(
y=RUN_VOL["codeflash_apr_projected"],
line_dash="dot",
line_color=GREEN,
opacity=0.5,
)
fig.add_annotation(
x="Feb '26",
y=21307,
text="Audit starts (Apr 9)",
showarrow=True,
arrowhead=2,
arrowcolor=ACCENT,
ax=40,
ay=-40,
font={"size": 12, "color": ACCENT},
)
fig.update_layout(
plot_bgcolor="rgba(0,0,0,0)",
paper_bgcolor="rgba(0,0,0,0)",
font={"family": FONT, "size": 13, "color": SLATE},
yaxis={"title": "Workflow Runs", "gridcolor": CARD_BORDER, "zeroline": False},
xaxis={"title": ""},
margin={"t": 20, "b": 60, "l": 70, "r": 20},
height=360,
showlegend=False,
)
return fig
def make_billing_chart():
"""Stacked bar: Enterprise minutes allotment vs overage."""
cats = ["Before (Feb)", "After (Apr)"]
included = [BILLING["enterprise_included_min"], BILLING["enterprise_included_min"]]
overage = [BILLING["overage_before_min"], BILLING["overage_after_min"]]
fig = go.Figure()
fig.add_trace(
go.Bar(
name="Included (50K)",
x=cats,
y=included,
marker_color=BLUE,
marker_cornerradius=6,
)
)
fig.add_trace(
go.Bar(
name="Overage",
x=cats,
y=overage,
marker_color=RED,
marker_cornerradius=6,
text=[f"{v:,} min\n(${v * 0.008:,.0f}/mo)" for v in overage],
textposition="outside",
textfont={"size": 12, "color": RED},
)
)
fig.update_layout(
barmode="stack",
plot_bgcolor="rgba(0,0,0,0)",
paper_bgcolor="rgba(0,0,0,0)",
font={"family": FONT, "size": 13, "color": SLATE},
yaxis={"title": "Billed Minutes/month", "gridcolor": CARD_BORDER, "zeroline": False},
xaxis={"title": ""},
margin={"t": 40, "b": 60, "l": 70, "r": 20},
legend={
"orientation": "h",
"yanchor": "bottom",
"y": 1.05,
"xanchor": "center",
"x": 0.5,
"font": {"size": 13},
},
height=380,
)
return fig
# ── Tab: Executive Summary ───────────────────────────────────────────────────
def _build_summary_tab():
return html.Div(
id="summary-view",
children=[
# Hero metrics
html.Div(
style={"display": "flex", "gap": "16px", "marginTop": "32px", "flexWrap": "wrap"},
children=[
hero_metric(f"~${BILLING['overage_saved_annual_usd']:,}/yr", "Overage Savings", "Enterprise minutes overage reduced 58%", GREEN),
hero_metric(f"{RUN_VOL['codeflash_reduction_pct']}%", "Fewer Runs", f"{RUN_VOL['codeflash_feb']:,}{RUN_VOL['codeflash_apr_projected']:,}/mo", ACCENT),
hero_metric("200+", "Forks Disabled", "GitHub Actions turned off org-wide", BLUE),
hero_metric("22 → 7", "Workflows Consolidated", "Single ci.yaml with gate job", PURPLE),
],
),
section(
"What We Found",
"Full CI/CD audit of the codeflash-ai GitHub org: 200+ forks and 2 main repos.",
),
# Key findings grid
html.Div(
style={"display": "grid", "gridTemplateColumns": "1fr 1fr", "gap": "16px"},
children=[
card([
html.Div("Fork CI Waste", style={"fontWeight": "700", "color": ACCENT, "fontSize": "16px", "marginBottom": "12px"}),
html.P(
"26 of 200+ forks were running GitHub Actions — Dependabot updates, upstream scheduled CI, "
"and failing workflows creating ~960 noise runs/month. kornia alone was 91% of fork CI cost "
"due to a daily macOS + Windows test matrix.",
style={"color": GRAY, "fontSize": "14px", "lineHeight": "1.6", "margin": "0"},
),
]),
card([
html.Div("Wildcard Path Triggers", style={"fontWeight": "700", "color": RED, "fontSize": "16px", "marginBottom": "12px"}),
html.P(
"All 12 E2E workflows used paths: ['**'] — any file change (README, docs) "
"triggered the full E2E suite. A single docs-only PR burned ~2 hours of compute.",
style={"color": GRAY, "fontSize": "14px", "lineHeight": "1.6", "margin": "0"},
),
]),
card([
html.Div("Ghost Workflows", style={"fontWeight": "700", "color": AMBER, "fontSize": "16px", "marginBottom": "12px"}),
html.P(
"13 workflow files had been deleted from the repo but their entries remained active in "
"GitHub Actions. These cluttered the Actions UI and created confusing status signals.",
style={"color": GRAY, "fontSize": "14px", "lineHeight": "1.6", "margin": "0"},
),
]),
card([
html.Div("Broken claude-code-action", style={"fontWeight": "700", "color": RED, "fontSize": "16px", "marginBottom": "12px"}),
html.P(
"v1.0.90 broke Bedrock OIDC auth. Every Claude Code run was failing with 403s. "
"60-100% failure rate on codeflash, 85% on codeflash-internal.",
style={"color": GRAY, "fontSize": "14px", "lineHeight": "1.6", "margin": "0"},
),
]),
],
),
section(
"Run Volume & Cost Impact",
"Workflow runs dropped 71%. Enterprise minutes overage cut from ~215K to ~90K/month.",
),
# Run volume + billing side by side
html.Div(
style={"display": "grid", "gridTemplateColumns": "1fr 1fr", "gap": "16px"},
children=[
card([
html.Div("Monthly Workflow Runs (codeflash)", style={"fontWeight": "700", "color": SLATE, "fontSize": "15px", "marginBottom": "12px"}),
dcc.Graph(figure=make_run_volume_chart(), config={"displayModeBar": False}),
]),
card([
html.Div("Enterprise Minutes Billing", style={"fontWeight": "700", "color": SLATE, "fontSize": "15px", "marginBottom": "12px"}),
html.P(
f"50K included minutes/month. Overage dropped from ~{BILLING['overage_before_min']:,} to ~{BILLING['overage_after_min']:,} min "
f"(${BILLING['overage_saved_monthly_usd']:,}/month saved).",
style={"color": GRAY, "fontSize": "13px", "lineHeight": "1.5", "margin": "0 0 12px"},
),
dcc.Graph(figure=make_billing_chart(), config={"displayModeBar": False}),
]),
],
),
section("Before vs After"),
card([dcc.Graph(figure=make_before_after_chart(), config={"displayModeBar": False})]),
section(
"Operational Improvements",
"The audit transformed CI from a maintenance burden to a self-service system.",
),
# Before/after comparison table
card([
html.Div(
style={"display": "grid", "gridTemplateColumns": "1fr auto auto", "gap": "0"},
children=[
# Header
html.Div("", style={"padding": "12px 16px"}),
html.Div("Before", style={"padding": "12px 16px", "fontWeight": "700", "color": RED, "fontSize": "13px", "textAlign": "right", "width": "200px"}),
html.Div("After", style={"padding": "12px 16px", "fontWeight": "700", "color": GREEN, "fontSize": "13px", "textAlign": "right", "width": "200px"}),
# Rows
*_comparison_row("Workflow files in repo", "22", "7"),
*_comparison_row("Required checks in branch protection", "13 individual", "1 gate job"),
*_comparison_row("Workflow-only PR merge", "Admin override", "Self-service"),
*_comparison_row("Non-code PR compute cost", "$1.85", "$0.001"),
*_comparison_row("Fork failing runs/month", "~960", "0"),
*_comparison_row("Ghost workflows in Actions UI", "13", "0"),
*_comparison_row("Branch protection model", "Legacy rules", "Repository rulesets"),
*_comparison_row("Dependabot test fixture noise", "70% failure rate", "Excluded"),
],
),
]),
],
)
def _comparison_row(label, before, after):
border = f"1px solid {CARD_BORDER}"
return [
html.Div(label, style={"padding": "12px 16px", "color": SLATE, "fontSize": "14px", "fontWeight": "600", "borderTop": border}),
html.Div(before, style={"padding": "12px 16px", "color": LIGHT_GRAY, "fontSize": "14px", "fontFamily": MONO, "textAlign": "right", "borderTop": border, "width": "200px"}),
html.Div(after, style={"padding": "12px 16px", "color": GREEN, "fontSize": "14px", "fontFamily": MONO, "fontWeight": "600", "textAlign": "right", "borderTop": border, "width": "200px"}),
]
# ── Tab: Full Detail ─────────────────────────────────────────────────────────
def _build_detail_tab():
pr_rows = []
for p in PRS_MERGED:
base = REPO_BASES[p["repo"]]
pr_rows.append({
"PR": f"[#{p['pr']}]({base}/{p['pr']})",
"Repo": p["repo"],
"Date": p["date"],
"Description": p["title"],
})
action_rows = [
{"Action": a["action"], "Date": a["date"], "Repo": a["repo"]}
for a in DIRECT_ACTIONS
]
fork_rows = [
{"Repo": f["repo"], "Runs": f"{f['runs']:,}", "Cost/yr": f"${f['cost_yr']}", "Runners": f["runners"], "Pattern": f["pattern"]}
for f in FORK_CI
]
finding_rows = []
for repo, items in FINDINGS.items():
for f in items:
pr_link = f"[#{f['pr']}]({REPO_BASES[repo]}/{f['pr']})" if f["pr"] else "Direct action"
finding_rows.append({
"Repo": repo,
"Finding": f["finding"],
"Impact": f["impact"],
"Fix": f["fix"],
"PR": pr_link,
})
return html.Div(
id="detail-view",
style={"display": "none"},
children=[
section("Fork CI Activity", "26 of 200+ forks had active GitHub Actions. Actions disabled on all forks 2026-04-23."),
# Fork charts side by side
html.Div(
style={"display": "grid", "gridTemplateColumns": "1fr 1fr", "gap": "16px"},
children=[
card([
html.Div("Runs by Repository", style={"fontWeight": "700", "color": SLATE, "fontSize": "15px", "marginBottom": "12px"}),
dcc.Graph(figure=make_fork_chart(), config={"displayModeBar": False}),
]),
card([
html.Div("Cost Breakdown", style={"fontWeight": "700", "color": SLATE, "fontSize": "15px", "marginBottom": "12px"}),
html.P(
"kornia is 91% of fork CI cost: daily scheduled test matrix across macOS ($0.08/min), Windows ($0.016/min), and Linux.",
style={"color": GRAY, "fontSize": "13px", "lineHeight": "1.5", "margin": "0 0 12px"},
),
dcc.Graph(figure=make_fork_cost_chart(), config={"displayModeBar": False}),
]),
],
),
# Fork table
html.Div(style=TABLE_WRAP, children=[
dash_table.DataTable(
data=fork_rows,
columns=[
{"name": "Repo", "id": "Repo"},
{"name": "Runs", "id": "Runs"},
{"name": "Cost/yr", "id": "Cost/yr"},
{"name": "Runners", "id": "Runners"},
{"name": "Pattern", "id": "Pattern"},
],
style_header=TABLE_HEADER,
style_cell=TABLE_CELL,
style_data=TABLE_DATA,
style_data_conditional=TABLE_DATA_CONDITIONAL,
style_as_list_view=True,
page_size=20,
),
]),
section("All Findings", "Categorized by repository."),
html.Div(style=TABLE_WRAP, children=[
dash_table.DataTable(
data=finding_rows,
columns=[
{"name": "Repo", "id": "Repo"},
{"name": "Finding", "id": "Finding"},
{"name": "Impact", "id": "Impact"},
{"name": "Fix", "id": "Fix"},
{"name": "PR", "id": "PR", "presentation": "markdown"},
],
style_header=TABLE_HEADER,
style_cell={**TABLE_CELL, "whiteSpace": "normal", "height": "auto"},
style_data=TABLE_DATA,
style_data_conditional=TABLE_DATA_CONDITIONAL,
style_as_list_view=True,
page_size=20,
css=[{"selector": "p", "rule": "margin: 0"}],
),
]),
section("PRs Merged", f"{len(PRS_MERGED)} pull requests across 2 repositories."),
html.Div(style=TABLE_WRAP, children=[
dash_table.DataTable(
data=pr_rows,
columns=[
{"name": "PR", "id": "PR", "presentation": "markdown"},
{"name": "Repo", "id": "Repo"},
{"name": "Date", "id": "Date"},
{"name": "Description", "id": "Description"},
],
style_header=TABLE_HEADER,
style_cell={**TABLE_CELL, "whiteSpace": "normal", "height": "auto"},
style_data=TABLE_DATA,
style_data_conditional=TABLE_DATA_CONDITIONAL,
style_as_list_view=True,
css=[{"selector": "p", "rule": "margin: 0"}],
),
]),
section("Direct Actions", "Non-PR changes applied during the audit."),
html.Div(style=TABLE_WRAP, children=[
dash_table.DataTable(
data=action_rows,
columns=[
{"name": "Action", "id": "Action"},
{"name": "Date", "id": "Date"},
{"name": "Repo", "id": "Repo"},
],
style_header=TABLE_HEADER,
style_cell={**TABLE_CELL, "whiteSpace": "normal", "height": "auto"},
style_data=TABLE_DATA,
style_data_conditional=TABLE_DATA_CONDITIONAL,
style_as_list_view=True,
css=[{"selector": "p", "rule": "margin: 0"}],
),
]),
section("Methodology"),
card([
html.Ol(
[
html.Li([html.Strong("Inventory"), "", html.Span("gh repo list codeflash-ai", style={"fontFamily": MONO, "fontSize": "13px"}), " to enumerate all 200+ repos, classify as fork vs primary"], style=_li_style()),
html.Li([html.Strong("Fork scan"), " — Query Actions run counts per fork since Apr 2025, identify 26 active forks"], style=_li_style()),
html.Li([html.Strong("Compute cost"), " — Sample job-level data (duration, runner type), calculate at GitHub rates: $0.008/min Linux, $0.016/min Windows, $0.08/min macOS"], style=_li_style()),
html.Li([html.Strong("Main repo audit"), " — List all workflows, check run history, failure rates, ghost detection, trigger configuration"], style=_li_style()),
html.Li([html.Strong("Root cause analysis"), " — Compare working vs broken runs by commit SHA and timestamp to pinpoint regressions"], style=_li_style()),
],
style={"paddingLeft": "20px", "margin": "0"},
),
]),
section("Monitoring"),
card([
html.Ul(
[
html.Li([html.Strong("claude-code-action"), " — unpin from v1.0.89 once anthropics/claude-code-action#1196 lands upstream"], style=_li_style()),
html.Li([html.Strong("Dependabot alerts"), " — 24 known vulnerabilities at audit time; new dependabot.yml targets real deps only"], style=_li_style()),
html.Li([html.Strong("Fork re-enable"), " — if a fork is needed: ", html.Code("echo '{\"enabled\":true}' | gh api --method PUT repos/codeflash-ai/<repo>/actions/permissions --input -", style={"fontFamily": MONO, "fontSize": "12px", "color": ACCENT})], style=_li_style()),
],
style={"paddingLeft": "20px", "margin": "0", "listStyleType": "'\\2022 '"},
),
]),
],
)
def _li_style():
return {"color": GRAY, "fontSize": "14px", "lineHeight": "1.7", "marginBottom": "8px"}
# ── Main layout ──────────────────────────────────────────────────────────────
_TAB_BTN_STYLE = {
"padding": "10px 24px",
"border": "none",
"borderRadius": "8px",
"cursor": "pointer",
"fontSize": "14px",
"fontWeight": "600",
"fontFamily": FONT,
"background": "transparent",
"color": GRAY,
"transition": "all 0.2s",
}
_TAB_BTN_ACTIVE = {**_TAB_BTN_STYLE, "background": ACCENT, "color": DARK}
def _main_layout():
return html.Div(
style={
"fontFamily": FONT,
"background": BG,
"color": SLATE,
"minHeight": "100vh",
"position": "relative",
},
children=[
html.Div(style=GRID_OVERLAY),
html.Div(
style={
"maxWidth": "1100px",
"margin": "0 auto",
"padding": "48px 32px 80px",
"position": "relative",
"zIndex": "1",
},
children=[
# Header
html.Div(
style={"textAlign": "center", "marginBottom": "8px"},
children=[
_logo_lockup(),
html.H1(
"CI/CD Audit Report",
style={
"fontSize": "36px",
"fontWeight": "800",
"color": WHITE,
"margin": "16px 0 8px",
"letterSpacing": "-0.02em",
},
),
html.P(
"codeflash-ai org — 200+ forks, 2 main repos",
style={
"fontSize": "16px",
"color": GRAY,
"margin": "0 0 4px",
},
),
html.P(
"April 923, 2026",
style={
"fontSize": "14px",
"color": LIGHT_GRAY,
"margin": "0",
"fontFamily": MONO,
},
),
],
),
# Tab buttons
html.Div(
style={"display": "flex", "justifyContent": "center", "margin": "40px 0 8px"},
children=[
html.Div(
style={
"display": "inline-flex",
"background": CARD_BG,
"borderRadius": "12px",
"padding": "4px",
"border": f"1px solid {CARD_BORDER}",
},
children=[
html.Button("Executive Summary", id="btn-summary", n_clicks=1, style=_TAB_BTN_ACTIVE),
html.Button("Full Detail", id="btn-detail", n_clicks=0, style=_TAB_BTN_STYLE),
],
),
],
),
_build_summary_tab(),
_build_detail_tab(),
# Footer
html.Div(
style={
"textAlign": "center",
"marginTop": "64px",
"paddingTop": "24px",
"borderTop": f"1px solid {CARD_BORDER}",
},
children=[
html.Div(
_logo_lockup("16px", "20px", "10px", "3px"),
style={"display": "flex", "justifyContent": "center", "marginBottom": "4px"},
),
html.P(
"CI/CD Audit Report — April 2026",
style={"color": LIGHT_GRAY, "fontSize": "13px", "margin": "0"},
),
],
),
],
),
],
)
# ── App ──────────────────────────────────────────────────────────────────────
app = Dash(
__name__,
meta_tags=[
{"name": "viewport", "content": "width=device-width, initial-scale=1"},
{"property": "og:title", "content": "Codeflash — CI/CD Audit Report"},
{
"property": "og:description",
"content": "CI audit of codeflash-ai org: 200+ forks disabled, 11 PRs merged, 960 failing runs/month eliminated",
},
],
suppress_callback_exceptions=True,
)
app.title = "Codeflash — CI/CD Audit Report"
app.index_string = """<!DOCTYPE html>
<html>
<head>
{%metas%}
<title>{%title%}</title>
<link rel="preconnect" href="https://fonts.googleapis.com">
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600;700;800&family=JetBrains+Mono:wght@400;600;700&display=swap" rel="stylesheet">
{%favicon%}
{%css%}
<style>
.dash-table-container .dash-cell a,
.dash-table-container .cell-markdown a,
.dash-table-container a,
.dash-spreadsheet a { color: #60a5fa !important; text-decoration: none !important; }
.dash-table-container a:hover,
.dash-spreadsheet a:hover { text-decoration: underline !important; }
</style>
</head>
<body>
{%app_entry%}
<footer>
{%config%}
{%scripts%}
{%renderer%}
</footer>
</body>
</html>"""
app.layout = _main_layout
# ── Toggle callback ──
clientside_callback(
"""
function(summary_c, detail_c) {
summary_c = summary_c || 0;
detail_c = detail_c || 0;
var base = {
"padding": "10px 24px", "border": "none", "borderRadius": "8px",
"cursor": "pointer", "fontSize": "14px", "fontWeight": "600",
"fontFamily": "'Inter', system-ui, -apple-system, sans-serif",
"transition": "all 0.2s"
};
var active = Object.assign({}, base, {"background": "#ffd227", "color": "#09090b"});
var inactive = Object.assign({}, base, {"background": "transparent", "color": "#a1a1aa"});
var show = {"display": "block"};
var hide = {"display": "none"};
var mx = Math.max(summary_c, detail_c);
if (detail_c === mx && detail_c > 0)
return [hide, show, inactive, active];
return [show, hide, active, inactive];
}
""",
Output("summary-view", "style"),
Output("detail-view", "style"),
Output("btn-summary", "style"),
Output("btn-detail", "style"),
Input("btn-summary", "n_clicks"),
Input("btn-detail", "n_clicks"),
)
server = app.server
if __name__ == "__main__":
app.run(
debug=os.getenv("DASH_DEBUG", "1") == "1",
port=int(os.getenv("PORT", "8051")),
)

View file

@ -0,0 +1,96 @@
{
"codeflash_base": "https://github.com/codeflash-ai/codeflash/pull",
"internal_base": "https://github.com/codeflash-ai/codeflash-internal/pull",
"audit_date": "2026-04-09",
"completion_date": "2026-04-23",
"org": "codeflash-ai",
"total_forks": 200,
"active_forks": 26,
"fork_runs_audit_period": 7012,
"fork_runs_monthly": 23373,
"fork_failing_monthly": 960,
"fork_cost_yr": 248,
"ghost_workflows_disabled": 13,
"workflows_before": 22,
"workflows_after": 7,
"required_checks_before": 13,
"required_checks_after": 1,
"skip_savings_per_pr": 1.85,
"fork_ci": [
{"repo": "sglang", "runs": 4936, "cost_yr": 4, "runners": "Linux", "pattern": "CI Failure Monitor (cron, skips on fork)"},
{"repo": "kornia", "runs": 308, "cost_yr": 226, "runners": "Linux + Windows + macOS", "pattern": "Daily scheduled CPU test matrix"},
{"repo": "ray", "runs": 527, "cost_yr": 3, "runners": "Linux", "pattern": "Dependabot + Stale PR bot"},
{"repo": "lerobot", "runs": 330, "cost_yr": 12, "runners": "Linux", "pattern": "Dependabot + Full Tests"},
{"repo": "next.js", "runs": 217, "cost_yr": 3, "runners": "Linux", "pattern": "Dependabot only"},
{"repo": "strapi", "runs": 118, "cost_yr": 1, "runners": "Linux", "pattern": "Dependabot (lodash x 20+ packages)"},
{"repo": "rasa", "runs": 85, "cost_yr": 1, "runners": "Linux", "pattern": "Dependabot"},
{"repo": "Rocket.Chat", "runs": 80, "cost_yr": 1, "runners": "Linux", "pattern": "Dependabot"},
{"repo": "nest", "runs": 73, "cost_yr": 1, "runners": "Linux", "pattern": "Dependabot (@nestjs/core x 20+ samples)"},
{"repo": "openclaw", "runs": 68, "cost_yr": 1, "runners": "Linux", "pattern": "Dependabot"},
{"repo": "appsmith", "runs": 64, "cost_yr": 1, "runners": "Linux", "pattern": "Dependabot"},
{"repo": "15 others", "runs": 206, "cost_yr": 1, "runners": "Linux", "pattern": "Dependabot / misc"}
],
"findings": {
"codeflash": [
{"finding": "Wildcard path triggers on 12 E2E workflows", "impact": "Every PR triggered ~2hrs of E2E tests", "fix": "Targeted path filters", "pr": 2025},
{"finding": "Broken claude-code-action (v1.0.90 Bedrock OIDC)", "impact": "60-100% failure rate on Claude Code workflows", "fix": "Pinned to v1.0.89", "pr": 2026},
{"finding": "Dependabot scanning test fixtures", "impact": "70% of Dependabot runs failing", "fix": "Added dependabot.yml excluding code_to_optimize/", "pr": 2027},
{"finding": "13 ghost workflows (deleted source files)", "impact": "Actions dashboard clutter, confusing signals", "fix": "gh workflow disable on all 13", "pr": null},
{"finding": "17 separate workflow files, 13 required checks", "impact": "Workflow-only PRs stuck at Pending, admin merge required", "fix": "Single ci.yaml with gate job", "pr": 2044},
{"finding": "No npm/Maven caching, duplicate workflows", "impact": "Slow JS/Java CI, redundant compute", "fix": "Consolidated + caching", "pr": 2050},
{"finding": "Outdated action versions, broken Windows paths", "impact": "CI failures on Windows matrix", "fix": "Upgraded actions + fixed paths", "pr": 2052},
{"finding": "Stale codeflash.yaml", "impact": "Superseded by codeflash-optimize.yaml", "fix": "Deleted", "pr": 2056}
],
"codeflash-internal": [
{"finding": "Deploy AI Service path included .github/workflows/**", "impact": "Any workflow edit triggered production deploy", "fix": "Scoped to actual service paths", "pr": 2588},
{"finding": "Claude Code workflow had no path filters", "impact": "Fired on every PR/comment", "fix": "Added paths-ignore", "pr": 2588},
{"finding": "Publish to PyPI permanently disabled (if: false)", "impact": "Skipped run noise on every push to main", "fix": "Disabled via API", "pr": 2588},
{"finding": "Broken claude-code-action (same v1.0.90 regression)", "impact": "85% failure rate", "fix": "Pinned to v1.0.89", "pr": 2587}
]
},
"prs_merged": [
{"pr": 2025, "repo": "codeflash", "date": "2026-04-09", "title": "Replace wildcard path triggers on 12 E2E workflows"},
{"pr": 2026, "repo": "codeflash", "date": "2026-04-09", "title": "Pin claude-code-action to v1.0.89 (Bedrock auth fix)"},
{"pr": 2027, "repo": "codeflash", "date": "2026-04-09", "title": "Exclude test fixtures from Dependabot"},
{"pr": 2044, "repo": "codeflash", "date": "2026-04-09", "title": "Consolidate 17 workflows into single ci.yaml with gate job"},
{"pr": 2047, "repo": "codeflash", "date": "2026-04-09", "title": "Path filters, validate-pr action, fetch-depth, continue-on-error"},
{"pr": 2050, "repo": "codeflash", "date": "2026-04-09", "title": "npm cache, Maven consolidation, remove duplicate workflow"},
{"pr": 2052, "repo": "codeflash", "date": "2026-04-10", "title": "Upgrade action versions, add uv cache, fix broken paths, DRY publish"},
{"pr": 2053, "repo": "codeflash", "date": "2026-04-10", "title": "Fix shell: bash for Windows conditional install"},
{"pr": 2056, "repo": "codeflash", "date": "2026-04-10", "title": "Delete disabled codeflash.yaml workflow"},
{"pr": 2587, "repo": "codeflash-internal", "date": "2026-04-09", "title": "Pin claude-code-action to v1.0.89"},
{"pr": 2588, "repo": "codeflash-internal", "date": "2026-04-09", "title": "Fix deploy path filter, Claude Code paths, disable PyPI"}
],
"direct_actions": [
{"action": "Disabled 13 ghost workflows", "date": "2026-04-09", "repo": "codeflash"},
{"action": "Disabled Publish to PyPI workflow", "date": "2026-04-09", "repo": "codeflash-internal"},
{"action": "Closed stale Dependabot PR #2012", "date": "2026-04-09", "repo": "codeflash"},
{"action": "Migrated branch protection to rulesets", "date": "2026-04-09", "repo": "codeflash"},
{"action": "Disabled Actions on 200+ forks", "date": "2026-04-23", "repo": "org-wide"}
],
"operational_before_after": {
"workflow_files": [22, 7],
"required_checks": [13, 1],
"non_code_pr_cost": [1.85, 0.001],
"fork_failing_runs_monthly": [960, 0],
"admin_merges_needed": true
},
"run_volume": {
"codeflash_feb": 21307,
"codeflash_apr_projected": 6219,
"codeflash_reduction_pct": 71,
"internal_feb": 2494,
"internal_apr_projected": 3864,
"total_eliminated_monthly": 16000
},
"billing": {
"enterprise_included_min": 50000,
"overage_before_min": 214872,
"overage_after_min": 89613,
"overage_saved_min": 125259,
"overage_saved_monthly_usd": 1002,
"overage_saved_annual_usd": 12025,
"non_code_pr_before_sec": 780,
"non_code_pr_after_sec": 8
}
}

View file

@ -0,0 +1,80 @@
"""Theme and styling constants for the Codeflash CI Audit report."""
# ── Colors (Codeflash dark - amber/zinc) ────────────────────────────────────
ACCENT = "#ffd227"
DARK = "#09090b"
CARD_BG = "rgba(16,20,28,0.7)" # dark navy/70 — readable over animated bg
CARD_BORDER = "rgba(63,63,70,0.35)" # zinc-700/35
SLATE = "#e4e4e7"
GRAY = "#a1a1aa"
LIGHT_GRAY = "#71717a"
BG = "#0d1117" # dark navy (codeflash.ai style)
WHITE = "#fafafa"
GREEN = "#4ade80"
LIGHT_GREEN = "rgba(74,222,128,0.12)"
RED = "#f87171"
LIGHT_RED = "rgba(248,113,113,0.12)"
AMBER = "#fbbf24"
BLUE = "#60a5fa"
PURPLE = "#a78bfa"
PINK = "#f472b6"
# ── Grid overlay ────────────────────────────────────────────────────────────
# Matches the roadmap page's subtle grid pattern.
GRID_BG_IMAGE = (
"linear-gradient(to right, currentColor 1px, transparent 1px),"
"linear-gradient(to bottom, currentColor 1px, transparent 1px)"
)
GRID_BG_SIZE = "48px 48px"
GRID_OVERLAY = {
"position": "fixed",
"top": 0,
"left": 0,
"right": 0,
"bottom": 0,
"backgroundImage": GRID_BG_IMAGE,
"backgroundSize": GRID_BG_SIZE,
"opacity": "0.05",
"pointerEvents": "none",
"zIndex": "0",
}
# ── Component styles ────────────────────────────────────────────────────────
CARD = {
"background": CARD_BG,
"borderRadius": "16px",
"padding": "28px 32px",
"border": f"1px solid {CARD_BORDER}",
}
FONT = "'Inter', system-ui, -apple-system, sans-serif"
MONO = "'JetBrains Mono', 'Menlo', monospace"
# ── Table styles ────────────────────────────────────────────────────────────
TABLE_HEADER: dict[str, str] = {
"backgroundColor": "rgba(24,24,27,0.8)",
"color": ACCENT,
"fontWeight": "600",
"fontSize": "13px",
"padding": "12px 16px",
"borderBottom": f"1px solid {CARD_BORDER}",
}
TABLE_CELL: dict[str, str] = {
"textAlign": "left",
"padding": "12px 16px",
"fontSize": "13px",
"fontFamily": FONT,
"border": "none",
"color": SLATE,
}
TABLE_DATA: dict[str, str] = {
"backgroundColor": "rgba(24,24,27,0.5)",
"color": SLATE,
}
TABLE_DATA_CONDITIONAL: list[dict[str, object]] = [
{"if": {"row_index": "odd"}, "backgroundColor": "rgba(31,31,35,0.6)"}
]
TABLE_WRAP: dict[str, str] = {
"borderRadius": "16px",
"overflow": "hidden",
"border": f"1px solid {CARD_BORDER}",
}