# Project export: Ramp

This document was generated by HackStack to give an AI agent context about a hackathon project. Sections are labeled with their provenance; content marked as truncated was cut to keep this document small.

## Project metadata

- Hackathon: UC Berkeley AI Hackathon 2026
- Tagline: Ramp finds accessibility issues axe misses, fixes the code with Claude, and opens a merge-ready PR.
- Devpost: https://devpost.com/software/ramp-2igw7p
- GitHub: https://github.com/yangzhang75/Ramp
- Video: https://www.youtube.com/embed/h6nY35pDjRk?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 2 GitHub contributor(s) — yangzhang75 (46 commits), kz930 (23 commits)

## Devpost submission (written by the team)

### Inspiration

1.3 billion people live with a disability. Whether they can use a website comes down to whether its code was written correctly — and the reality is that most of the web is broken for them. Over 96% of the top one million homepages have detectable accessibility failures. Existing tools weren't built to close this gap. Industry standards like axe and Lighthouse can tell you whether an attribute exists — but they're blind to whether it's meaningful, and they only ever hand you a report. An image with alt="DSC_1042.JPG" passes axe cleanly, yet to a screen-reader user that filename means nothing. Ramp was built to catch what standard tools can't see — and to actually fix it.

### What it does

Ramp is an end-to-end accessibility platform that audits a rendered web page, scores its compliance, and automatically fixes the code and opens a merge-ready pull request — dropping straight into the GitHub workflow teams already use. Where existing tools stop at mechanical checks and static reports, Ramp pairs a real benchmark with an agent harness and an autonomous fix loop that turns evaluation into action. It has three core capabilities: Semantic judgment axe cannot do. Across three realistic pages, axe-core reported 0 issues — everything passed. Ramp found 12 semantic issues axe let through silently: a product image labeled DSC_1042.JPG, a button labeled "button", and a link reading "click here". Only a model that understands language can judge whether an accessible name is meaningful. Detection that depends on rendering. Many accessibility failures only appear once a page is actually rendered. Color contrast, for example, lives in external CSS — a model reading raw HTML can't see it. On a contrast-focused evaluation suite, the same model (GPT-4o-mini) reading only the HTML caught 40% of issues; equipped with Ramp's harness — which renders the page and measures contrast for real — it caught 92%. Across the full 18-page suite, the harness wins on both recall (84.5%) and precision (89.3%). This is the core thesis: accessibility lives in what the user experiences, not just in the source code. An autonomous fix loop. Ramp doesn't stop at flagging issues — it fixes them. It takes a real open-source project, edits the code with Claude Code, re-verifies the result with axe, and opens a real pull request — raising compliance from 92 to 100, with axe violations going from 2 to 0. We don't send a report. We send the fix. How Claude Code powered Ramp Claude Code wasn't just a tool we integrated — it was the force multiplier that made the project possible within a hackathon timeframe. It contributed in three compounding ways. Claude Code is the agent at the heart of our fix loop. When the audit surfaces a finding, Claude Code edits the source, makes a minimal targeted change, and self-verifies — turning a detected issue into real, working code. The image alt text DSC_1042.JPG becomes "Aero Runner shoe"; the unlabeled icon button receives aria-label="Search". The output isn't an alert — it's a merge-ready pull request, indistinguishable from a human-authored accessibility fix. Claude Code built the system alongside us. The harness, control plane, sandbox checkout, fixer, and dashboard came together during the hackathon because Claude Code worked as a collaborator. We developed parallel slices while focusing on architecture and scoring, giving the codebase the surface area of a much larger team's project. Claude Code makes semantic fixes possible. Detecting that alt="image" is meaningless is only half the problem. Deriving the right replacement from the surrounding context — and applying it as a clean diff — is what closes the loop. Without an agent capable of long-horizon, context-aware editing, this system wouldn't exist.

### How we built it

Ramp is a TypeScript monorepo that connects model providers, sandboxed execution, accessibility tooling, and GitHub workflows through one control plane. Auditing: Playwright and axe-core, combined with a rendered accessibility tree, screen-reader serialization, and real contrast measurement. Multi-provider model layer: The Vercel AI SDK, allowing models to be evaluated on identical tasks. The fix loop: Sandboxed checkout → Claude Code headless edits → axe re-verification → real pull request through Octokit. Benchmark and scoring: A11y-Bench tasks plus a hand-built HTML-live evaluation suite, scored deterministically. Observability: The complete fix loop is instrumented with Sentry, with every audit, fix, and pull request represented as a traced span. Dashboard: React, Vite, and Tailwind, showing axe-versus-Ramp comparisons and before-and-after compliance scores.

### Challenges we ran into

Stitching auditing, sandboxed checkout, Claude Code, and GitHub into one reproducible loop required significant infrastructure work. Our most important insight came from an evaluation failure: scoring a harness against single-PR ground truth can actually penalize a thorough auditor. A real page may contain dozens of accessibility issues, but the answer key contains only the one issue fixed by the original pull request. As a result, a system that finds additional real problems can incorrectly appear imprecise. This taught us that precision is only meaningful on fully annotated pages. It reshaped our evaluation approach and led us to build a fully annotated HTML-live suite where the harness's advantage could be measured fairly.

### Accomplishments we're proud of

Built a capability axe fundamentally lacks: judging whether accessible names are meaningful, not merely present — axe: 0 findings, Ramp: 12 findings on the same pages. Demonstrated a measurable render-dependent advantage: 40% to 92% contrast-detection recall using the same model, with only the harness changing. Built a closed loop from detection to a merge-ready pull request on real open-source projects. Covered issues including landmarks, form labels, button names, alternative text, and color contrast. Curated a benchmark of 51 real accessibility tasks, each tied to a reviewable pull request.

### What we learned

Tools and prompts are a real lever on capability: the same model behaves very differently when equipped with a domain-specific harness. The pull request is the right interface for accessibility work because review, CI, collaboration, and merging are already solved by the tools developers use. The value isn't in reporting more issues — it's in being precise and actually fixing them.

### What's next

We plan to integrate Ramp directly into CI so that newly introduced accessibility issues can be automatically reproduced, fixed, and submitted upstream as pull requests. We also want to move the fix engine to a pure API path so anyone can run it using their own keys, without requiring a local agent installation. Finally, we plan to extend Ramp's source-level analysis capabilities, building on the source-code tasks already included in our benchmark. The goal is to make accessibility something developers complete by reviewing a single pull request.

## README (from the GitHub repository)

# Ramp

**Accessibility audit → fix → PR. axe detects; Ramp understands and fixes.**

Lighthouse and axe-core *find* WCAG violations and stop at a report. Ramp closes the loop:
it audits a real rendered page, reasons about the WCAG criterion, **writes the fix, verifies
it with axe-core, and opens a merge-ready pull request** — and it catches semantic issues axe
is blind to (alt text that just says `"image"`, links that say `"click here"`).

> **axe: 0 violations · Ramp: 12 semantic issues axe can't see** (across 3 demo pages).

---

## Three pillars

| Pillar | Package | What it does |
|---|---|---|
| **A11y-Bench** | `packages/bench` | 51 ground-truth tasks mined from real merged a11y PRs; scores **naked LLM vs harness** on recall **and** precision, split by `html-live` / `source-code`. |
| **Harness** | `packages/harness` | Drives a headless page through Playwright + axe-core + accessibility tree + screen-reader simulation + contrast/focus inspectors + **semantic review**; an LLM agent reasons over the evidence (`runAudit`). |
| **Auto-fix loop** | `packages/control-plane` | sandbox checkout → audit → **Claude Code** fix → **axe verify (before/after score)** → **GitHub PR**. Sentry monitors the loop. |

## Real fix PRs (verified, before → after)

| Repo | Fix | Score | PR |
|---|---|---|---|
| `bad.html` (fixture) | alt + contrast + button names | **60 → 96** | [yangzhang75/Ramp#7](https://github.com/yangzhang75/Ramp/pull/7) |
| semantic (fixture) | meaningless alt/link/button names axe passes | semantic 5 → 0 | [yangzhang75/Ramp#10](https://github.com/yangzhang75/Ramp/pull/10) |
| `aigov-ops…` (real OSS) | landmarks + skip link + `<main>` | **92 → 100** | [PR#1](https://github.com/yangzhang75/aigov-ops-open-source-vendor-rfi-rapp-johnston-june-2026/pull/1) |
| `caelaria` (real OSS) | unlabeled `<select>` controls | **84 → 92** | [PR#1](https://github.com/yangzhang75/caelaria/pull/1) |
| `Whatifarcade` (real OSS) | form label + `<main>` landmark | **96 → 100** | [PR#1](https://github.com/yangzhang75/Whatifarcade/pull/1) |

*PRs are opened on forks — Ramp audits and fixes the real page without spamming upstream maintainers.*

## Architecture

```mermaid
flowchart LR
  USER["Frontend repo / URL"] --> API

  subgraph CP["Control Plane · packages/control-plane"]
    API["node:http API<br/>POST /audit · /benchmark"]
    DB[("Drizzle + SQLite<br/>runs · findings · scores")]
    API --- DB
  end

  subgraph H["Harness · packages/harness"]
    PW["Playwright page"]
    TOOLS["axe-core · a11y tree<br/>screen-reader · contrast<br/>focus-order · semantic review"]
    AGENT(["LLM audit agent<br/>runAudit · gpt-4o-mini"])
    PW --> TOOLS --> AGENT
  end

  subgraph FL["Fix Loop · packages/control-plane"]
    SB["sandbox checkout<br/>fork @ base commit"]
    FIX["Claude Code fix<br/>claude -p"]
    VER["axe verify<br/>before → after score"]
    PR["GitHub PR · Octokit<br/>opened on fork"]
    SB --> FIX --> VER --> PR
  end

  API --> PW
  AGENT -->|"findings + before score"| DB
  AGENT --> SB
  VER -->|"after score"| DB
  PR --> GH["GitHub<br/>merge-ready PR"]
  SENTRY{{"Sentry monitoring"}} -.-> CP
  SENTRY -.-> H
  SENTRY -.-> FL
```

## Quick start

```bash
pnpm install

# 1. Self-contained demo — repair bad.html and score it (free, no API keys)
pnpm --filter @ramp/control-plane fix:demo                    # 60 → 96

# 2. Detection benchmark — naked LLM vs harness, recall + precision
pnpm --filter @ramp/bench score:fixtures                      # needs OPENAI_API_KEY
pnpm --filter @ramp/scoring leaderboard

# 3. Real-repo fix loop — fork → audit → fix → verify → open PR
TASK_ID=ramp-048 pnpm --filter @ramp/control-plane fix:repo   # needs OPENAI_API_KEY + GITHUB_TOKEN

# 4. Web UI (landing + demo dashboard)
pnpm dev:control-plane     # :8787 — API (optional, for Live Run / benchmark tabs)
pnpm dev:dashboard         # :5173 — Home tab + axe vs Ramp · Auto-fix · Scores · …
```

## Tech stack

Playwright · axe-core · Vercel AI SDK (`ai` + `@ai-sdk/openai`, gpt-4o-mini) ·
Claude Code (`claude -p`, headless fixer) · Drizzle ORM + SQLite · React + Vite ·
Sentry · Octokit · `node:http` · TypeScript + pnpm workspaces.

## Monorepo layout

| Path | Role |
|---|---|
| `packages/shared` | Types · Drizzle schema · DB client |
| `packages/harness` | Audit tools + `runAudit` agent |
| `packages/scoring` | Recall/precision metrics + leaderboard |
| `packages/bench` | A11y-Bench tasks + miners/curators |
| `packages/control-plane` | HTTP API + fix loop + GitHub PRs |
| `apps/dashboard` | React + Vite site: **Home** (product landing) + interactive demo tabs |

---

*Detect → Score → Fix → Validate → Pull Request. The artifact isn't a report — it's a reviewable PR.*


## Detected evidence (automated analysis)

Indexed codebase: 119 recognized source files, 557 KB.
- Anthropic (technology) — detected in the code
- CSS (language) — detected in the code
- HTML (language) — detected in the code
- OpenAI (technology) — detected in the code
- React (technology) — detected in the code
- Tailwind CSS (technology) — detected in the code
- TypeScript (language) — detected in the code
- Vercel AI SDK (technology) — detected in the code
- Node.js (technology) — claimed on Devpost, not found in the code

## Codebase structure (from repository index)

### Files (120 of 201)

```
.env.example
.gitignore
apps/dashboard/index.html
apps/dashboard/package.json
apps/dashboard/src/App.tsx
apps/dashboard/src/components/ArchitectureDiagram.tsx
apps/dashboard/src/components/BackendNotice.tsx
apps/dashboard/src/components/FixOutcomesSection.tsx
apps/dashboard/src/components/ui/badge.tsx
apps/dashboard/src/components/ui/button.tsx
apps/dashboard/src/components/ui/card.tsx
apps/dashboard/src/components/ui/tabs.tsx
apps/dashboard/src/data/auto-fix-results.json
apps/dashboard/src/data/complex-precision.json
apps/dashboard/src/data/fix-outcomes.json
apps/dashboard/src/data/vs-axe-report.json
apps/dashboard/src/hooks/useRampData.ts
apps/dashboard/src/index.css
apps/dashboard/src/lib/api.ts
apps/dashboard/src/lib/utils.ts
apps/dashboard/src/main.tsx
apps/dashboard/src/pages/AutoFixResultsPage.tsx
apps/dashboard/src/pages/HowItWorksPage.tsx
apps/dashboard/src/pages/LandingPage.tsx
apps/dashboard/src/pages/LiveRunPage.tsx
apps/dashboard/src/pages/PRCardPage.tsx
apps/dashboard/src/pages/ScoresPage.tsx
apps/dashboard/src/pages/VsAxePage.tsx
apps/dashboard/src/vite-env.d.ts
apps/dashboard/tsconfig.app.json
apps/dashboard/tsconfig.json
apps/dashboard/tsconfig.node.json
apps/dashboard/vite.config.ts
CONTRIBUTING.md
groupmate1.md
groupmate2.md
package.json
packages/bench/data/candidates.jsonl
packages/bench/data/fixture-precision-runs.json
packages/bench/data/fixtures-live/account-settings.css
packages/bench/data/fixtures-live/account-settings.html
packages/bench/data/fixtures-live/analytics-report.css
packages/bench/data/fixtures-live/analytics-report.html
packages/bench/data/fixtures-live/checkout-summary.css
packages/bench/data/fixtures-live/checkout-summary.html
packages/bench/data/fixtures-live/ground-truth.json
packages/bench/data/fixtures-live/invoice.css
packages/bench/data/fixtures-live/invoice.html
packages/bench/data/fixtures-live/news-article.css
packages/bench/data/fixtures-live/news-article.html
packages/bench/data/fixtures-live/product-catalog.css
packages/bench/data/fixtures-live/product-catalog.html
packages/bench/data/fixtures-live/support-ticket.css
packages/bench/data/fixtures-live/support-ticket.html
packages/bench/data/fixtures-live/team-roster.css
packages/bench/data/fixtures-live/team-roster.html
packages/bench/data/fixtures/admin-console.html
packages/bench/data/fixtures/article.html
packages/bench/data/fixtures/booking.html
packages/bench/data/fixtures/dashboard.html
packages/bench/data/fixtures/ground-truth.json
packages/bench/data/fixtures/knowledge-base.html
packages/bench/data/fixtures/landing.html
packages/bench/data/fixtures/pricing.html
packages/bench/data/fixtures/shop.html
packages/bench/data/fixtures/signin.html
packages/bench/data/fixtures/team-settings.html
packages/bench/data/html-annotations.json
packages/bench/data/seeds.txt
packages/bench/data/tasks/ramp-001.json
packages/bench/data/tasks/ramp-002.json
packages/bench/data/tasks/ramp-003.json
packages/bench/data/tasks/ramp-004.json
packages/bench/data/tasks/ramp-005.json
packages/bench/data/tasks/ramp-006.json
packages/bench/data/tasks/ramp-007.json
packages/bench/data/tasks/ramp-008.json
packages/bench/data/tasks/ramp-009.json
packages/bench/data/tasks/ramp-010.json
packages/bench/data/tasks/ramp-011.json
packages/bench/data/tasks/ramp-012.json
packages/bench/data/tasks/ramp-013.json
packages/bench/data/tasks/ramp-014.json
packages/bench/data/tasks/ramp-015.json
packages/bench/data/tasks/ramp-016.json
packages/bench/data/tasks/ramp-017.json
packages/bench/data/tasks/ramp-018.json
packages/bench/data/tasks/ramp-019.json
packages/bench/data/tasks/ramp-020.json
packages/bench/data/tasks/ramp-021.json
packages/bench/data/tasks/ramp-022.json
packages/bench/data/tasks/ramp-023.json
packages/bench/data/tasks/ramp-024.json
packages/bench/data/tasks/ramp-025.json
packages/bench/data/tasks/ramp-026.json
packages/bench/data/tasks/ramp-027.json
packages/bench/data/tasks/ramp-028.json
packages/bench/data/tasks/ramp-029.json
packages/bench/data/tasks/ramp-030.json
packages/bench/data/tasks/ramp-031.json
packages/bench/data/tasks/ramp-032.json
packages/bench/data/tasks/ramp-033.json
packages/bench/data/tasks/ramp-034.json
packages/bench/data/tasks/ramp-035.json
packages/bench/data/tasks/ramp-036.json
packages/bench/data/tasks/ramp-037.json
packages/bench/data/tasks/ramp-038.json
packages/bench/data/tasks/ramp-039.json
packages/bench/data/tasks/ramp-040.json
packages/bench/data/tasks/ramp-041.json
packages/bench/data/tasks/ramp-042.json
packages/bench/data/tasks/ramp-043.json
packages/bench/data/tasks/ramp-044.json
packages/bench/data/tasks/ramp-045.json
packages/bench/data/tasks/ramp-046.json
packages/bench/data/tasks/ramp-047.json
packages/bench/data/tasks/ramp-048.json
packages/bench/data/tasks/ramp-049.json
packages/bench/data/tasks/ramp-050.json
packages/bench/data/tasks/ramp-051.json
[81 more files omitted for size]
```

### Dependencies

- apps/dashboard/package.json: @radix-ui/react-slot@^1.2.3, @radix-ui/react-tabs@^1.1.12, @ramp/shared@workspace:*, @tailwindcss/vite@^4.1.11, @types/react@^19.1.8, @types/react-dom@^19.1.6, @vitejs/plugin-react@^4.6.0, class-variance-authority@^0.7.1, clsx@^2.1.1, lucide-react@^0.525.0, react@^19.1.0, react-dom@^19.1.0, recharts@^3.0.2, tailwind-merge@^3.3.1, tailwindcss@^4.1.11, typescript@^5.7.2, vite@^7.0.0
- package.json: @types/node@^22.10.2, typescript@^5.7.2
- packages/bench/package.json: @octokit/rest@^21.1.1, @ramp/harness@workspace:*, @ramp/scoring@workspace:*, @ramp/shared@workspace:*, tsx@^4.22.4
- packages/control-plane/package.json: @octokit/rest@^21.1.1, @ramp/harness@workspace:*, @ramp/scoring@workspace:*, @ramp/shared@workspace:*, @sentry/node@^10.59.0, drizzle-orm@^0.38.2, tsx@^4.22.4
- packages/harness/package.json: @ai-sdk/anthropic@^3.0.85, @ai-sdk/openai@^2.0.52, @axe-core/playwright@^4.11.3, @ramp/shared@workspace:*, ai@^6.0.207, playwright@^1.61.0, tsx@^4.22.4, zod@^4.4.3
- packages/scoring/package.json: @ai-sdk/anthropic@^3.0.85, @ai-sdk/openai@^2.0.52, @ramp/harness@workspace:*, @ramp/shared@workspace:*, ai@^6.0.207, tsx@^4.22.4, zod@^4.4.3
- packages/shared/package.json: @types/better-sqlite3@^7.6.12, better-sqlite3@^11.7.0, drizzle-kit@^0.30.1, drizzle-orm@^0.38.2

### Recent commits (newest first)

- rerun html-live 3x — stable averages (harness 84.5/89.3 vs naked 69.0/79.5)
- Merge pull request #13 from yangzhang75/sentry-monitoring
- Merge pull request #11 from yangzhang75/semantic-demo
- Merge pull request #9 from yangzhang75/semantic-review
- Merge pull request #8 from yangzhang75/fix-loop
- Merge pull request #6 from yangzhang75/control-plane-scoring
- Merge pull request #5 from yangzhang75/control-plane-audit
- Merge pull request #4 from yangzhang75/harness-audit
- Merge pull request #3 from yangzhang75/rounded-sombrero
- Merge pull request #1 from yangzhang75/rounded-sombrero
- docs(repo): add environment variable template
- docs(skill): initial Ramp skill draft
- docs(repo): team planning notes
- docs(repo): contributing guide
- feat(dashboard): vs-axe demo report data
- feat(dashboard): static demo metrics data
- feat(bench): html-live scoring and curation CLI
- feat(bench): mining and curation CLI
- feat(bench): extend annotated task set
- feat(bench): expand annotated task set

## Key source files (fetched from GitHub, selected and truncated for size)

### CONTRIBUTING.md

```markdown
# Contributing to Ramp

Ramp is a pnpm + TypeScript (ESM) monorepo. This guide keeps a fast-moving
hackathon team from stepping on each other.

## Layout

```
packages/
  shared/         Types (BenchTask/Finding/FixResult/Score), Drizzle SQLite
                  schema + getDb(), and the runAudit/runFixLoop signatures.
  harness/        Turns a general LLM into a WCAG expert (rule library, DOM
                  reasoning, screen-reader sim, contrast tools). Implements runAudit.
  scoring/        Compliance scoring + benchmark recall metrics.
  bench/          A11y-Bench: annotated benchmark tasks + grading.
  control-plane/  Orchestrates Detect→Score→Fix→Validate→PR. Implements runFixLoop.
apps/
  dashboard/      Landing (Home tab) + demo UI: axe vs Ramp, auto-fix PRs, scores, live run.
```

## Directory ownership

Edit your own directory. To change someone else's, open a PR and tag them — do
not push directly into another owner's package.

| Directory                  | Owner            | Scope                                            |
| -------------------------- | ---------------- | ------------------------------------------------ |
| `packages/shared`          | Platform / Lead  | Types, DB schema, shared signatures. Changes here ripple everywhere — coordinate before editing. |
| `packages/harness`         | Harness owner    | WCAG rules, DOM reasoning, screen-reader sim, contrast, `runAudit`. |
| `packages/scoring`         | Scoring owner    | Score model + recall metrics.                    |
| `packages/bench`           | Benchmark owner  | A11y-Bench fixtures + grading.                   |
| `packages/control-plane`   | Pipeline owner   | Orchestration, fix loop, sandbox validation, PR creation. |
| `apps/dashboard`           | Frontend owner   | Dashboard UI.                                    |
| root config (`tsconfig*`, `pnpm-workspace.yaml`, `.gitignore`, CI) | Platform / Lead | Tooling / build wiring. |

`packages/shared` is the shared contract: anyone can depend on it, but any
edit to its types or schema must be announced so dependents update together.

## Setup

```bash
pnpm install
pnpm build      # tsc -b across all packages (project references)
```

`pnpm typecheck` is an alias for the same `tsc -b`. `pnpm clean` removes build
output.

## Git workflow

- **Always `git pull --rebase` before you push.** Never merge-commit `main`
  into your branch; keep history linear.
  ```bash
  git pull --rebase origin main
  ```
- Work on a branch, open a PR, get a quick review, then merge.
- Resolve rebase conflicts in your own files; ping the owner for theirs.

## Commit message prefixes

Use Conventional Commits, scoped to the package you touched:

```
<type>(<scope>): <summary>
```

- **types:** `feat`, `fix`, `chore`, `docs`, `refactor`, `test`, `build`, `ci`
- **scopes:** `shared`, `harness`, `scoring`, `bench`, `control-plane`,
  `dashboard`, or `repo` for root/tooling changes.

Examples:

```
feat(harness): add color-contrast compu
[truncated — 470 more characters]
```

### RAMP_同学并行任务清单.md

```markdown
# RAMP · 同学(Codex)并行任务清单
你负责证明路径。下面把活拆成独立任务,标了【能否现在开】和【依赖】。
**能并行的就同时多开几个 Codex session 一起跑,别一条线慢慢走。**
你的目录:`packages/bench`、`packages/scoring`、`apps/dashboard`

> 队友进度(你可以直接用的):runAudit(审计功能)、/audit 接口、合规分都已在 main。
> 所以你的 scoring 不用搞假替身,直接调真的。

---

# 🔴 先做(5 分钟,所有任务的前提)

**P0 · 环境 + 字段对齐**
```
1. clone https://github.com/yangzhang75/Ramp.git,pnpm install,pnpm build 确认全绿。
2. 建 .env(repo 根,别提交):GITHUB_TOKEN、OPENAI_API_KEY(去 platform.openai.com 充$10~15)、
   GOOGLE_API_KEY(可选,Gemini 免费额度)、ANTHROPIC_API_KEY(用队友那个)。
3. 打印 packages/shared 里 ViolationType 的全部取值,和 BenchTask/AnnotatedFinding/
   Finding/Score 四个类型的字段。贴出来。
```
**记死**:违规类别 `type` 必须用 ViolationType 里的值,跟队友 harness 一致。队友现在在用的有:
`missing_alt_text`、`icon_button_accessible_names`、`low_color_contrast`、
`missing_form_labels`、`missing_landmarks`(完整以打印出来的为准)。
文件字段:你的 `file` ↔ 队友的 `sourceFile`。

---

# ===== 🟢 可立即并行的三个任务(P0 做完就同时开)=====

## 任务 A · 手挑 10 个种子【现在就能开 · 不依赖任何人 · 不花钱】
在 GitHub 找已合并的、真修无障碍的 PR(diff 含 aria-label/alt/role/contrast/label/tabindex),
把 `owner/repo#prnumber` 抄进 `packages/bench/data/seeds.txt`,一行一个,凑 10 个。
标准:真 a11y 修复、diff 小(1–3 文件)、前端仓库。
> 这是人工活,你自己做,不用 Codex。10 分钟。

## 任务 B · 挖候选脚本(mine)【现在就能开 · 不依赖 · 不烧 AI 钱】
```
在 packages/bench 写 src/mine.ts(ESM,tsx 跑)。用 octokit + GITHUB_TOKEN:
1. 搜已合并 PR,关键词:accessibility, a11y, aria-label, "alt text", wcag, "role=",
   "focus order", contrast。优先前端仓库。指数退避处理限速。
2. 同时读 data/seeds.txt(任务A的),无条件纳入。
3. 每个 PR 取:repo、pr_number、base_commit、fix_commit、标题、unified diff。
4. 追加写 data/candidates.jsonl,每行 {repo,pr_number,base_commit,fix_commit,title,diff}。
跑完告诉我多少条、种子占几条。
```

## 任务 C · dashboard 界面骨架【现在就能开 · 先用假数据 · 不依赖真数据】
```
把 apps/dashboard 搭成 Vite + React + TS + Tailwind + shadcn/ui(pnpm create vite 起标准结构)。
先用写死的假数据做出三个屏的 UI 骨架:
1. Live Run:列出 Finding(type/severity/evidence),重点显示 evidence 里的读屏证据和对比度。
2. Scores:柱状图 naked vs harness;合规分 before→after 大数字;leaderboard 表。
3. PR Card:展示一个修复 PR 卡片(标题/diff/before-after)。
深色、干净。先把样子做出来,真数据接口后面接。做完 pnpm dev 起来截图。
```

> A、B、C 互不依赖,**同时开三个 Codex session 跑**(C 是前端、B 是脚本、A 你手动)。

---

# ===== 🟡 有前置的任务(前置好了再开)=====

## 任务 D · curate 成测试题【等 B 挖到候选后开】
```
在 packages/bench 写 src/curate.ts(ESM/tsx)。对 data/candidates.jsonl 每行:
1. 浅克隆 repo,checkout 到 base_commit(修复前)。
2. 读 diff,用 OpenAI(便宜档模型即可)生成符合 shared 里 BenchTask 类型的对象:
   - id "ramp-"+编号;repoUrl/branch/framework
   - expectedFindings: 对 diff 每处修复一条 AnnotatedFinding:
     type(必须 ViolationType 合法值,和队友同一套)、wcagRule、file、line?、expectedFix
   - createdAt: ISO
3. 写成 data/tasks/<id>.json。
4. 按 type 分层,打印分布,别让 missing_alt_text 占满。
5. 多候选并行处理。
先冲 20~30 题,贴数量和分布给我。
```
✅ 抽 3 个生成的 task 人工看 type/wcagRule/file 对不对。

## 任务 E · scoring 打分【等 D 有题后开 · 出第一组真数字 🔴】
```
在 packages/scoring 写 src/score.ts(ESM/tsx)。import @ramp/shared 类型 + @ramp/harness 的 runAudit。
1. detection:遍历 data/tasks/ 每题,调 runAudit 拿 Finding[]。
   命中 = type 相等 且 wcagRule 相等 且 task.file === finding.sourceFile。算 recall/precision。
2. naked vs harness:naked=只丢 HTML 不给工具让模型直接列;harness=调完整 runAudit。
   各出一行 Score。
[truncated — 927 more characters]
```

### package.json

```
{
  "name": "ramp",
  "version": "0.0.0",
  "private": true,
  "type": "module",
  "description": "Ramp — turn accessibility reports into merge-ready pull requests.",
  "packageManager": "pnpm@11.7.0",
  "engines": {
    "node": ">=20"
  },
  "scripts": {
    "build": "tsc -b",
    "typecheck": "tsc -b",
    "clean": "tsc -b --clean",
    "dev:dashboard": "pnpm --filter @ramp/dashboard dev",
    "dev:control-plane": "pnpm --filter @ramp/control-plane serve"
  },
  "devDependencies": {
    "@types/node": "^22.10.2",
    "typescript": "^5.7.2"
  }
}

```

### packages/shared/package.json

```
{
  "name": "@ramp/shared",
  "version": "0.0.0",
  "private": true,
  "type": "module",
  "main": "./dist/index.js",
  "types": "./dist/index.d.ts",
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "default": "./dist/index.js"
    },
    "./db": {
      "types": "./dist/db/index.d.ts",
      "default": "./dist/db/index.js"
    }
  },
  "scripts": {
    "build": "tsc -b",
    "clean": "tsc -b --clean"
  },
  "dependencies": {
    "better-sqlite3": "^11.7.0",
    "drizzle-orm": "^0.38.2"
  },
  "devDependencies": {
    "@types/better-sqlite3": "^7.6.12",
    "drizzle-kit": "^0.30.1"
  }
}

```

### packages/bench/package.json

```
{
  "name": "@ramp/bench",
  "version": "0.0.0",
  "private": true,
  "type": "module",
  "main": "./dist/index.js",
  "types": "./dist/index.d.ts",
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "default": "./dist/index.js"
    }
  },
  "scripts": {
    "build": "tsc -b",
    "clean": "tsc -b --clean",
    "mine": "tsx --env-file=../../.env src/mine.ts",
    "curate": "tsx --env-file=../../.env src/curate.ts",
    "curate:html": "tsx src/curate-html.ts",
    "score:fixtures": "tsx src/score-fixtures.ts",
    "score:html-live": "tsx src/score-html-live.ts"
  },
  "dependencies": {
    "@octokit/rest": "^21.1.1",
    "@ramp/shared": "workspace:*",
    "@ramp/scoring": "workspace:*",
    "@ramp/harness": "workspace:*"
  },
  "devDependencies": {
    "tsx": "^4.22.4"
  }
}

```

### apps/dashboard/package.json

```
{
  "name": "@ramp/dashboard",
  "version": "0.0.0",
  "private": true,
  "type": "module",
  "scripts": {
    "dev": "vite --port 5173",
    "build": "tsc -p tsconfig.app.json --noEmit && vite build",
    "preview": "vite preview",
    "clean": "tsc -b --clean"
  },
  "dependencies": {
    "@radix-ui/react-slot": "^1.2.3",
    "@radix-ui/react-tabs": "^1.1.12",
    "@ramp/shared": "workspace:*",
    "class-variance-authority": "^0.7.1",
    "clsx": "^2.1.1",
    "lucide-react": "^0.525.0",
    "react": "^19.1.0",
    "react-dom": "^19.1.0",
    "recharts": "^3.0.2",
    "tailwind-merge": "^3.3.1"
  },
  "devDependencies": {
    "@tailwindcss/vite": "^4.1.11",
    "@types/react": "^19.1.8",
    "@types/react-dom": "^19.1.6",
    "@vitejs/plugin-react": "^4.6.0",
    "tailwindcss": "^4.1.11",
    "typescript": "^5.7.2",
    "vite": "^7.0.0"
  }
}

```

### packages/control-plane/package.json

```
{
  "name": "@ramp/control-plane",
  "version": "0.0.0",
  "private": true,
  "type": "module",
  "main": "./dist/index.js",
  "types": "./dist/index.d.ts",
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "default": "./dist/index.js"
    }
  },
  "scripts": {
    "build": "tsc -b",
    "clean": "tsc -b --clean",
    "serve": "tsx src/server-main.ts",
    "fix:demo": "tsx src/scripts/fix-demo.ts",
    "fix:repo": "tsx src/scripts/fix-repo-demo.ts",
    "fix:semantic": "tsx src/scripts/fix-semantic-demo.ts",
    "test:pr": "tsc -b && node --env-file-if-exists=../../.env dist/scripts/test-pr.js"
  },
  "dependencies": {
    "@octokit/rest": "^21.1.1",
    "@ramp/harness": "workspace:*",
    "@ramp/scoring": "workspace:*",
    "@ramp/shared": "workspace:*",
    "@sentry/node": "^10.59.0",
    "drizzle-orm": "^0.38.2"
  },
  "devDependencies": {
    "tsx": "^4.22.4"
  }
}

```

### packages/scoring/package.json

```
{
  "name": "@ramp/scoring",
  "version": "0.0.0",
  "private": true,
  "type": "module",
  "main": "./dist/index.js",
  "types": "./dist/index.d.ts",
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "default": "./dist/index.js"
    }
  },
  "scripts": {
    "build": "tsc -b",
    "clean": "tsc -b --clean",
    "score": "tsx --env-file=../../.env src/score.ts",
    "leaderboard": "tsx --env-file=../../.env src/leaderboard.ts",
    "leaderboard:openai": "tsx --env-file=../../.env src/leaderboard.ts",
    "test:match": "tsx --test src/match.test.ts src/audit-context.test.ts",
    "regrade": "tsx src/regrade-batch.ts"
  },
  "dependencies": {
    "@ai-sdk/anthropic": "^3.0.85",
    "@ai-sdk/openai": "^2.0.52",
    "@ramp/harness": "workspace:*",
    "@ramp/shared": "workspace:*",
    "ai": "^6.0.207",
    "zod": "^4.4.3"
  },
  "devDependencies": {
    "tsx": "^4.22.4"
  }
}

```

### packages/harness/package.json

```
{
  "name": "@ramp/harness",
  "version": "0.0.0",
  "private": true,
  "type": "module",
  "main": "./dist/index.js",
  "types": "./dist/index.d.ts",
  "exports": {
    ".": {
      "types": "./dist/index.d.ts",
      "default": "./dist/index.js"
    }
  },
  "scripts": {
    "build": "tsc -b",
    "clean": "tsc -b --clean",
    "probe:axe": "tsx src/probe-axe.ts",
    "probe:tree": "tsx src/probe-tree.ts",
    "probe:sr": "tsx src/probe-sr.ts",
    "probe:inspect": "tsx src/probe-inspect.ts",
    "probe:audit": "tsx src/probe-audit.ts",
    "probe:semantic": "tsx src/probe-semantic.ts",
    "vs:axe": "tsx src/vs-axe.ts",
    "vs:axe:suite": "tsx src/vs-axe-suite.ts"
  },
  "dependencies": {
    "@ai-sdk/anthropic": "^3.0.85",
    "@ai-sdk/openai": "^2.0.52",
    "@axe-core/playwright": "^4.11.3",
    "@ramp/shared": "workspace:*",
    "ai": "^6.0.207",
    "playwright": "^1.61.0",
    "zod": "^4.4.3"
  },
  "devDependencies": {
    "tsx": "^4.22.4"
  }
}

```

### apps/dashboard/src/main.tsx

```typescript
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import { App } from "./App.js";
import "./index.css";

createRoot(document.getElementById("root")!).render(
  <StrictMode>
    <App />
  </StrictMode>,
);

```

[114 more indexed source files omitted to keep this export small. The full file list is in the Codebase structure section above.]