# Project export: BuildProof

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: BuildProof verifies whether a hackathon project’s claims are actually backed by its code.
- Devpost: https://devpost.com/software/buildproof-9cg5ev
- GitHub: https://github.com/sanghyun6/BuildProof
- Team: 1 GitHub contributor(s) — sanghyun6 (1 commits)

## Devpost submission (written by the team)

### Inspiration

AI projects are being built and launched faster than ever. Every day, people share new demos, hackathon submissions, open-source tools, startup prototypes, and AI agent projects that claim to use advanced systems like RAG, multi-agent workflows, MCP, computer vision, voice agents, or custom model routing. But the people evaluating these projects — judges, sponsors, investors, recruiters, potential users, and even teammates — usually do not have time to manually inspect every repository and verify every technical claim. A demo can look impressive. A README can sound convincing. A pitch can say the project uses multiple advanced technologies. But the real question is: Is the claim actually backed by implementation evidence in the code? We started BuildProof in the hackathon setting because this problem is especially visible there, but the broader problem is technical trust. As AI-generated projects and rapid prototypes become more common, we need faster ways to verify whether a project’s story matches its implementation.

### What it does

BuildProof is an AI authenticity auditor that checks whether a project’s technical claims are supported by its GitHub code. A user can provide a project description, Devpost-style writeup, README text, or GitHub repository. BuildProof extracts the project’s technical claims, scans the GitHub repository, and gathers evidence from source files, dependencies, package files, file structure, README content, and missing implementation signals. It then produces an authenticity report showing: What the project claims to do What evidence exists in the repository Whether the evidence comes from real source code, dependencies, file structure, README text, or absence of implementation A weighted authenticity score LLM judge reasoning TokenRouter / MiniMax-M3 powered claim extraction and judging Optional Anthropic comparison to show where judges agree or disagree BuildProof is designed for hackathon judges, sponsors, investors, recruiters, and anyone who needs to quickly evaluate whether a technical project is real without spending 30 minutes manually reading the repo.

### How we built it

BuildProof is built as a Next.js app with a TypeScript audit pipeline. The pipeline has several stages: First, BuildProof extracts technical claims from project descriptions, Devpost-style writeups, or README-style input. When available, it uses TokenRouter with MiniMax-M3 for LLM-based claim extraction, with deterministic fallback behavior when keys are missing. Second, BuildProof scans the GitHub repository. It collects repository metadata, README content, package files, dependency information, file-tree signals, and source-file snippets. Third, BuildProof runs implementation-signal detectors. We upgraded the detector layer from simple keyword matching to reusable implementation-signal analysis across MCP, RAG, and multi-agent claims. The detectors distinguish between weak README-only mentions and stronger implementation evidence such as dependencies, imports, tool registration patterns, retrieval code, vector database usage, agent orchestration patterns, and source-file usage. Fourth, BuildProof scores evidence with a weighted model. Source code evidence counts more than package.json evidence, package evidence counts more than file-tree evidence, and README-only evidence is treated as weaker. Missing evidence is surfaced as absence evidence instead of being ignored. Finally, BuildProof uses an LLM judge to produce a verdict and rationale. TokenRouter / MiniMax-M3 is the primary model integration. We also added an optional judge comparison mode that runs Anthropic and TokenRouter on the same compressed evidence context and displays agreement or disagreement per claim.

### Challenges we ran into

One major challenge was avoiding a simple “LLM reads README and guesses” product. That would be easy to build, but not very trustworthy. We wanted BuildProof to ground its judgments in concrete repository evidence, so we had to build a real scanning and evidence pipeline. Another challenge was evidence quality. A README mention is not the same as source code implementation. A dependency is not the same as actual usage. A file name is not the same as a working feature. We had to design a weighted scoring system and detector layer that treats different evidence sources differently. We also had to make the app work even when LLM keys are missing. BuildProof includes deterministic fallback paths so the core audit still works without relying entirely on external model calls. Another challenge was sponsor integration depth. We wanted TokenRouter to be more than a single API call, so we added provider abstraction, smoke tests, safe logging, fallback behavior, model configuration, and a judge comparison panel that lets users compare TokenRouter / MiniMax-M3 against Anthropic on the same evidence.

### Accomplishments we're proud of

We are proud that BuildProof became a real evidence-based audit pipeline instead of just a wrapper around an LLM. Some highlights: End-to-end GitHub repo scanning TokenRouter / MiniMax-M3 integration for claim extraction and judging Optional Anthropic vs TokenRouter judge comparison Weighted authenticity scoring based on evidence strength Implementation-signal detectors for MCP, RAG, and multi-agent claims Safe fallback behavior when LLM providers are unavailable Compression-aware evidence context before judging A clear report UI showing claims, evidence, verdicts, scores, traces, and integration status A growing test suite covering provider selection, scoring, judge comparison, compression behavior, and implementation-signal detection We are especially proud of the judge comparison feature because it lets users “audit the auditor” by seeing where two model providers agree or disagree.

### What we learned

We learned that verifying AI project claims is harder than simply asking an LLM if something sounds real. The most important part is evidence quality. A project can mention “RAG,” “multi-agent,” or “MCP” in a README, but the real question is whether the repository contains dependencies, imports, source usage, configuration files, and implementation patterns that support those claims. We also learned that this problem is bigger than hackathons. As more AI prototypes, agent demos, and open-source projects appear, the cost of technical due diligence increases. People need faster ways to separate real implementation from polished descriptions. Finally, we learned that transparency matters. Users should not just see a score; they should see why the score exists, what evidence was used, which provider judged it, and where the system was uncertain.

### What's next

Next, we want to make BuildProof more useful for anyone who needs fast technical due diligence. Planned improvements include: Deeper static analysis for more claim categories Better Devpost and project-page ingestion Stronger repository analysis beyond file snippets, including import graphs and AST-level signals More model comparison options through TokenRouter Better report sharing for judges, investors, recruiters, and teams A public benchmark of real, exaggerated, and unsupported project claims Optional CI integration so teams can run BuildProof before publishing or submitting a project Organization-level dashboards for reviewing many projects at once Long term, we imagine BuildProof as a trust layer for technical demos: a fast way to check whether a project’s story is supported by the code behind it.

## README (from the GitHub repository)

No README available.

## Detected evidence (automated analysis)

Indexed codebase: 87 recognized source files, 570 KB.
- Anthropic (technology) — detected in the code
- CSS (language) — detected in the code
- JavaScript (language) — detected in the code
- Next.js (technology) — detected in the code
- Python (language) — detected in the code
- React (technology) — detected in the code
- Tailwind CSS (technology) — detected in the code
- TypeScript (language) — detected in the code
- Node.js (technology) — claimed on Devpost, not found in the code
- AI coding agent: Claude Code — evidence: config files committed to the repository

## Codebase structure (from repository index)

### Files (94 of 94)

```
.env.example
.eslintrc.json
.gitignore
adapters/compression/provider.ts
adapters/compression/theTokenCompanyCompressor.ts
adapters/compression/types.ts
adapters/github/mockScanner.ts
adapters/github/realScanner.ts
adapters/github/types.ts
adapters/ingest/browserbaseProjectIngestor.ts
adapters/ingest/mockProjectIngestor.ts
adapters/ingest/types.ts
adapters/llm/anthropicClaimExtractor.ts
adapters/llm/anthropicClaimJudge.ts
adapters/llm/provider.ts
adapters/llm/tokenrouterClaimExtractor.ts
adapters/llm/tokenrouterClaimJudge.ts
adapters/llm/types.ts
adapters/trace/sentryTraceAdapter.ts
adapters/trace/types.ts
app/api/audit/route.ts
app/api/smoke/tokenrouter/route.ts
app/api/status/route.ts
app/globals.css
app/layout.tsx
app/page.tsx
ARCHITECTURE_AUDIT.md
band_agents/_runtime.py
band_agents/agent_config.yaml.example
band_agents/check_band_setup.py
band_agents/claim_prosecutor.py
band_agents/evidence_defender.py
band_agents/lead_judge.py
band_agents/prompts/claim_prosecutor.md
band_agents/prompts/evidence_defender.md
band_agents/prompts/lead_judge.md
band_agents/prompts/repo_forensics.md
band_agents/README.md
band_agents/repo_forensics.py
band_agents/requirements.txt
band_agents/run_all.py
band_agents/smoke_band_agents.py
CLAUDE.md
components/ClaimCard.tsx
components/DetectorSummary.tsx
components/EvidenceItem.tsx
components/ScoreBar.tsx
components/VerdictBadge.tsx
data/mockReport.ts
DEMO.md
detectors/computerVision.ts
detectors/implementationSignals.ts
detectors/mcp.ts
detectors/multiAgent.ts
detectors/rag.ts
detectors/realtime.ts
detectors/scan.ts
detectors/voice.ts
HANDOFF.md
IMPLEMENTATION_REVIEW.md
lib/bandCourtPacket.ts
lib/integrationStatus.ts
lib/tokenEstimate.ts
lib/tokenRouterClient.ts
lib/trace.ts
next-env.d.ts
next.config.ts
package.json
pipeline/applySafety.ts
pipeline/compareJudges.ts
pipeline/compressEvidenceContext.ts
pipeline/extractClaims.ts
pipeline/generateReport.ts
pipeline/index.ts
pipeline/ingestProject.ts
pipeline/judgeClaims.ts
pipeline/matchEvidence.ts
pipeline/runDetectors.ts
pipeline/scanRepo.ts
pipeline/scoreAuthenticity.ts
postcss.config.js
scripts/compressionBenchmark.ts
scripts/compressionDemo.ts
scripts/testBandCourtPacket.ts
scripts/testCompressionProvider.ts
scripts/testImplementationSignals.ts
scripts/testJudgeComparison.ts
scripts/testProviderSelection.ts
scripts/testScoreAuthenticity.ts
tailwind.config.ts
TODO.md
tsconfig.json
types/pipeline.ts
utils/parseGitHubUrl.ts
```

### Dependencies

- band_agents/requirements.txt: band-sdk[anthropic]@>=1.0.0, python-dotenv@>=1.0.0
- package.json: @anthropic-ai/sdk@^0.105.0, @browserbasehq/sdk@^2.14.1, @sentry/node@^10.59.0, @types/node@^20, @types/react@^19, @types/react-dom@^19, autoprefixer@^10.0.1, eslint@^8, eslint-config-next@15.3.3, next@^16.2.9, playwright-core@^1.61.0, postcss@^8, react@^19.0.0, react-dom@^19.0.0, tailwindcss@^3.4.1, typescript@^5

### Recent commits (newest first)

- BuildProof

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

### CLAUDE.md

```markdown
# BuildProof — Claude Code Guide

## Product Summary

BuildProof is an AI project credibility auditor. It checks whether technical claims made in a Devpost page, README, or pitch are actually supported by implementation evidence in the project's GitHub repository.

**Core concept:** Claim → Evidence → Verdict

**Example:**
- Claim: "Uses MCP"
- Evidence: README mentions MCP / package.json has no @modelcontextprotocol/sdk / no MCP server or client code found
- Verdict: Unsupported by repository evidence

## Build Levels

| Level | Input method | Status |
|-------|-------------|--------|
| 2 | Manual text + GitHub URL input | Build first |
| 3 | Single Devpost/project URL (Browserbase) | Add later |

Always make Level 2 work before wiring Level 3.

## Core Pipeline

```
ingestProject
  → extractClaims
  → scanRepo
  → runDetectors
  → matchEvidence
  → scoreAuthenticity
  → judgeClaims
  → applySafety
  → generateReport
```

Each stage must be independently callable and testable. Each external integration must be behind an adapter.

## MVP Detectors

1. **Multi-agent detector** — looks for agent orchestration patterns, multi-agent frameworks
2. **MCP detector** — checks for @modelcontextprotocol/sdk, MCP server/client code
3. **RAG / vector DB detector** — looks for vector store imports, embedding calls, retrieval patterns
4. **Real-time / streaming detector** — checks for streaming APIs, WebSocket usage, SSE
5. **Voice / audio detector** — looks for audio processing libraries, speech APIs
6. **Computer vision / video AI detector** — checks for CV libraries, vision model calls

## Safety Wording Rules

**Never use:**
- fake
- lying
- scam
- fraud
- deceptive

**Use evidence-based language instead:**
- "No implementation evidence found"
- "Unsupported by repository evidence"
- "Partially supported"
- "Strongly supported"
- "README-only claim"

All verdict text must pass through `applySafety` before surfacing to the user.

## Architecture Rules

1. **Adapter-first:** Every external service (GitHub API, LLM API, Browserbase, Redis, Arize, Sentry, Band) must be behind an adapter interface. The app must work with mock/local adapters before any real integration is wired.
2. **Mock before real:** Each adapter must have a working mock. Real adapters are opt-in via environment variables.
3. **Level 2 before Level 3:** Manual input fallback must always work, even after Browserbase is added.
4. **TypeScript strict mode throughout.** No `any`. Co-locate tests with source files.
5. **One feature per session.** Read HANDOFF.md at session start. Implement only what is listed as next. Update TODO.md and HANDOFF.md before stopping.

## External Technology Candidates

| Service | Role | Adapter interface |
|---------|------|-------------------|
| GitHub API / Octokit | Repo scanning | `RepoAdapter` |
| LLM API | Claim extraction, evidence judging | `LLMAdapter` |
| Browserbase | Devpost page ingestion | `BrowserAdapter` |
| Redis | Evidence memory, audit caching | `
[truncated — 793 more characters]
```

### TODO.md

```markdown
# BuildProof — Implementation Order

Legend: `[ ]` not started · `[~]` in progress · `[x]` done

---

## Phase 0 — Project Docs
- [x] Create CLAUDE.md, TODO.md, HANDOFF.md

## Phase 1 — Mock Audit Dashboard
- [x] Scaffold Next.js (or Vite + React) project with TypeScript strict mode
- [x] Hard-coded sample audit result (one project, six detector verdicts)
- [x] Verdict display: claim, evidence list, verdict badge (Strongly Supported / Partially Supported / Unsupported)
- [x] Basic layout: project header, claim cards, overall score bar

## Phase 2 — Mock Core Pipeline
- [x] Define TypeScript interfaces: `Claim`, `Evidence`, `Verdict`, `AuditReport`, `ProjectInput`, `RepoScan`, `DetectorResult`, `ClaimWithEvidence`, `ScoredClaim`
- [x] Implement pipeline stages as pure functions with mock data flowing through
- [x] `ingestProject` (mock) → `extractClaims` (keyword) → `scanRepo` (mock) → `runDetectors` (mock)
- [x] `matchEvidence` → `scoreAuthenticity` → `judgeClaims` → `applySafety` → `generateReport`
- [x] Wire pipeline to dashboard — replace hard-coded data with pipeline output
- [x] `extractClaims` uses keyword matching on user text — returns only detected claim categories
- [x] Empty state shown when no claims are detected
- [x] Keyword matching uses word boundaries for single-word terms to prevent substring false positives
- [ ] Unit tests for each pipeline stage

## Phase 3 — GitHub Repo Scanner
- [x] GitHub URL parser (`utils/parseGitHubUrl.ts`) — handles HTTPS and SSH URLs, returns owner/repo/normalizedUrl or null
- [x] Define `RepoScannerAdapter` interface (`adapters/github/types.ts`)
- [x] Implement `mockScanner` (`adapters/github/mockScanner.ts`) — returns fixture file tree, package.json, README
- [x] `scanRepo` uses URL parser + mock scanner; returns `source: "invalid-url"` for unparseable URLs
- [x] Implement `realScanner` using native fetch, no Octokit (env-gated by `GITHUB_TOKEN`; falls back to `source: "unavailable"` on error)
- [x] `scanRepo`: fetch real file tree, README, package.json, selected source snippets via GitHub REST API
- [x] Move pipeline execution server-side: `app/api/audit/route.ts` → `runPipeline()`; `page.tsx` uses `fetch("/api/audit")`
- [x] `ScanSource` extended with `"unavailable"`; `AuditReport` exposes `scanSource`; UI shows scan status note
- [ ] File content cache to avoid redundant API calls

## Phase 4 — Six Static Detectors
- [x] Detector helper utilities (`detectors/scan.ts`)
- [x] Multi-agent detector (`detectors/multiAgent.ts`) — real RepoScan analysis
- [x] MCP detector (`detectors/mcp.ts`) — real RepoScan analysis
- [x] RAG / vector DB detector (`detectors/rag.ts`) — real RepoScan analysis
- [x] Real-time / streaming detector (`detectors/realtime.ts`) — real RepoScan analysis
- [x] Voice / audio detector (`detectors/voice.ts`) — real RepoScan analysis
- [x] Computer vision / video AI detector (`detectors/computerVision.ts`) — real RepoScan analysis
- [x] `runDetectors` wires all six real detectors; u
[truncated — 22384 more characters]
```

### package.json

```
{
  "name": "buildproof",
  "version": "0.1.0",
  "private": true,
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "eslint . --ext .ts,.tsx,.js,.jsx",
    "typecheck": "tsc --noEmit",
    "test": "npx tsx scripts/testProviderSelection.ts && npx tsx scripts/testScoreAuthenticity.ts && npx tsx scripts/testJudgeComparison.ts && npx tsx scripts/testCompressionProvider.ts && npx tsx scripts/testImplementationSignals.ts && npx tsx scripts/testBandCourtPacket.ts"
  },
  "dependencies": {
    "@anthropic-ai/sdk": "^0.105.0",
    "@browserbasehq/sdk": "^2.14.1",
    "@sentry/node": "^10.59.0",
    "next": "^16.2.9",
    "playwright-core": "^1.61.0",
    "react": "^19.0.0",
    "react-dom": "^19.0.0"
  },
  "devDependencies": {
    "@types/node": "^20",
    "@types/react": "^19",
    "@types/react-dom": "^19",
    "autoprefixer": "^10.0.1",
    "eslint": "^8",
    "eslint-config-next": "15.3.3",
    "postcss": "^8",
    "tailwindcss": "^3.4.1",
    "typescript": "^5"
  }
}

```

### band_agents/requirements.txt

```
band-sdk[anthropic]>=1.0.0
python-dotenv>=1.0.0

```

### app/layout.tsx

```typescript
import type { Metadata } from "next";
import { Inter } from "next/font/google";
import "./globals.css";

const inter = Inter({ subsets: ["latin"], variable: "--font-inter" });

export const metadata: Metadata = {
  title: "BuildProof — AI Project Credibility Auditor",
  description:
    "Check whether technical claims in a project's pitch are supported by implementation evidence in its GitHub repository.",
};

export default function RootLayout({
  children,
}: {
  children: React.ReactNode;
}) {
  return (
    <html lang="en" className={inter.variable}>
      <body className="bg-gray-950 text-gray-100 min-h-screen antialiased font-[var(--font-inter)]">
        {children}
      </body>
    </html>
  );
}

```

### pipeline/index.ts

```typescript
import type { AuditReport, IngestMeta, ProjectInput } from "../types/pipeline";
import { TraceCollector } from "../lib/trace";
import { sentryTraceAdapter } from "../adapters/trace/sentryTraceAdapter";
import { ingestProject } from "./ingestProject";
import { extractClaims } from "./extractClaims";
import { scanRepo } from "./scanRepo";
import { runDetectors } from "./runDetectors";
import { matchEvidence } from "./matchEvidence";
import { scoreAuthenticity } from "./scoreAuthenticity";
import { judgeClaims } from "./judgeClaims";
import { applySafety } from "./applySafety";
import { generateReport } from "./generateReport";

export async function runPipeline(
  input: ProjectInput,
  options?: { ingestMeta?: IngestMeta },
): Promise<AuditReport> {
  const collector = new TraceCollector();

  // Trace: input received
  const hasGithubUrl = input.githubUrl.trim().length > 0;
  collector.add(
    "input-received",
    "success",
    hasGithubUrl
      ? `Project text received with GitHub URL`
      : `Project text received (no GitHub URL)`,
    { hasGithubUrl, textLength: input.projectText.length },
  );

  // Trace: ingest mode
  if (options?.ingestMeta) {
    const meta = options.ingestMeta;
    collector.add(
      "project-url-ingestion",
      meta.source === "browserbase" ? "success" : "fallback",
      meta.source === "browserbase"
        ? `Browserbase ingestion succeeded (status: ${meta.status})`
        : `Project URL ingestion used demo fixture data`,
      { source: meta.source, status: meta.status },
    );
  } else {
    collector.add(
      "project-url-ingestion",
      "skipped",
      "Manual mode — no project URL ingestion",
    );
  }

  const ingested = ingestProject(input);

  // Claim extraction
  const { claims, source: claimExtractionSource } = await extractClaims(ingested);
  const extractionIsLLM =
    claimExtractionSource === "llm" ||
    claimExtractionSource === "llm-anthropic" ||
    claimExtractionSource === "llm-tokenrouter";
  const extractionProvider =
    claimExtractionSource === "llm-tokenrouter" ? "TokenRouter (MiniMax-M3)" : "Anthropic";
  collector.add(
    "claim-extraction",
    claimExtractionSource === "keyword-fallback" ? "fallback" : "success",
    extractionIsLLM
      ? `LLM (${extractionProvider}) extracted ${claims.length} claim(s)`
      : claimExtractionSource === "keyword-fallback"
        ? `LLM failed — keyword fallback found ${claims.length} claim(s)`
        : `Keyword matcher found ${claims.length} claim(s)`,
    { source: claimExtractionSource, claimCount: claims.length },
  );

  // GitHub scan
  const scan = await scanRepo(ingested);
  const scanStatus =
    scan.source === "github-api"
      ? "success"
      : scan.source === "invalid-url"
        ? "skipped"
        : "fallback";
  collector.add(
    "github-scan",
    scanStatus,
    scan.source === "github-api"
      ? `GitHub API scan succeeded (${scan.fileTree.length} files indexed)`
      : scan.source === "invalid-url"
        ? "No valid GitHub URL — repository not scanned"
        : "GitHub scan unavailable — evidence based on text only",
    { source: scan.source, fileCount: scan.fileTree.length },
  );

  // Detectors
  const detectorResults = runDetectors(claims, scan);
  collector.add(
    "detectors-run",
    "success",
    `${detectorResults.length} detector(s) run across ${claims.length} claim(s)`,
    { detectorCount: detectorResults.length },
  );

  const matched = matchEvidence(claims, detectorResults);
  const scored = scoreAuthenticity(matched);

  // Judge
  const {
    verdicts: judged,
    source: judgeSource,
    compression,
    comparison,
  } = await judgeClaims(scored, scan.source);

  if (compression) {
    const compressionStatus = compression.fallbackUsed
      ? "fallback"
      : compression.source === "disabled"
        ? "skipped"
        : "success";
    collector.add(
      "evidence-compression",
      compressionStatus,
      compression.source === "disabled"
        ? "Evidence compression disabled — raw context sent to LLM judge"
        : compression.fallbackUsed
          ? `Compression: The Token Company unavailable — local claim-aware compressor used (${compression.percentReduction}% reduction)`
          : compression.source === "the-token-company"
            ? `Compression: The Token Company reduced judge context by ${compression.percentReduction}%`
            : `Compression: local claim-aware compressor reduced judge context by ${compression.percentReduction}%`,
      {
        source: compression.source,
        rawEstimatedTokens: compression.rawEstimatedTokens,
        compressedEstimatedTokens: compression.compressedEstimatedTokens,
        percentReduction: compression.percentReduction,
        fallbackUsed: compression.fallbackUsed,
      },
    );
  }

  const judgeIsLLM =
    judgeSource === "llm" ||
    judgeSource === "llm-anthropic" ||
    judgeSource === "llm-tokenrouter";
  const judgeProvider = judgeSource === "llm-tokenrouter" ? "TokenRouter (MiniMax-M3)" : "Anthropic";
  collector.add(
    "judge",
    judgeSource === "deterministic-fallback" ? "fallback" : "success",
    judgeIsLLM
      ? `LLM judge (${judgeProvider}) evaluated ${judged.length} claim(s)`
      : judgeSource === "deterministic-fallback"
        ? `LLM judge failed — deterministic fallback applied`
        : `Deterministic judge applied to ${judged.length} claim(s)`,
    { source: judgeSource, verdictCount: judged.length },
  );

  // Comparison (optional; only when JUDGE_COMPARISON=on and both keys present)
  if (comparison) {
    const compStatus =
      comparison.status === "success"
        ? "success"
        : comparison.status === "failed"
          ? "error"
          : "fallback";
    const rateText =
      comparison.agreementRate !== null
        ? `${comparison.agreementRate}% agreement (${comparison.agreedCount}/${comparison.comparedCount})`
        : "no agreement rate available";
    const anthropicLabel = comparison.anthropi
[truncated — 1961 more characters]
```

### app/api/status/route.ts

```typescript
import { NextResponse } from "next/server";
import { getIntegrationStatus } from "../../../lib/integrationStatus";

export async function GET(): Promise<NextResponse> {
  return NextResponse.json(getIntegrationStatus());
}

```

### app/api/audit/route.ts

```typescript
import { NextRequest, NextResponse } from "next/server";
import { runPipeline } from "../../../pipeline";
import { browserbaseProjectIngestor } from "../../../adapters/ingest/browserbaseProjectIngestor";
import { mockProjectIngestor } from "../../../adapters/ingest/mockProjectIngestor";
import { captureSentryError } from "../../../adapters/trace/sentryTraceAdapter";
import type { ProjectInput } from "../../../types/pipeline";

type RequestBody = Record<string, unknown>;

export async function POST(req: NextRequest): Promise<NextResponse> {
  let body: RequestBody;
  try {
    body = (await req.json()) as RequestBody;
  } catch {
    return NextResponse.json({ error: "Invalid request body" }, { status: 400 });
  }

  try {
    // Mode B: project URL audit
    if (typeof body.projectUrl === "string") {
      const projectUrl = body.projectUrl.trim();
      if (!projectUrl) {
        return NextResponse.json({ error: "projectUrl must not be empty" }, { status: 400 });
      }

      const hasBrowserbaseKeys =
        !!process.env.BROWSERBASE_API_KEY && !!process.env.BROWSERBASE_PROJECT_ID;

      // Try Browserbase first (returns null if env vars missing or ingestion fails)
      let ingestResult = await browserbaseProjectIngestor.ingest({ projectUrl });
      const browserbaseFailed = hasBrowserbaseKeys && ingestResult === null;

      // Fall back to mock if Browserbase was not used or failed
      if (ingestResult === null) {
        const mockResult = await mockProjectIngestor.ingest({ projectUrl });
        if (mockResult === null) {
          return NextResponse.json(
            { error: "Project URL could not be ingested" },
            { status: 422 },
          );
        }
        ingestResult = browserbaseFailed
          ? {
              ...mockResult,
              warnings: [
                "Browserbase ingestion encountered an error — showing demo fixture data as fallback.",
                ...mockResult.warnings,
              ],
            }
          : mockResult;
      }

      if (ingestResult === null) {
        return NextResponse.json(
          { error: "Project URL could not be ingested" },
          { status: 422 },
        );
      }

      const input: ProjectInput = {
        projectText: ingestResult.description,
        githubUrl: ingestResult.githubUrl,
      };

      const report = await runPipeline(input, {
        ingestMeta: {
          title: ingestResult.title,
          builtWith: ingestResult.builtWith,
          source: ingestResult.source,
          status: ingestResult.status,
          warnings: ingestResult.warnings,
        },
      });
      return NextResponse.json(report);
    }

    // Mode A: manual audit
    if (typeof body.projectText === "string" && typeof body.githubUrl === "string") {
      const input: ProjectInput = {
        projectText: body.projectText,
        githubUrl: body.githubUrl,
      };
      const report = await runPipeline(input);
      return NextResponse.json(report);
    }

    return NextResponse.json(
      { error: "Request must include projectUrl (URL mode) or projectText + githubUrl (manual mode)" },
      { status: 400 },
    );
  } catch (err) {
    await captureSentryError(err);
    const message = err instanceof Error ? err.message : "Pipeline error";
    return NextResponse.json({ error: message }, { status: 500 });
  }
}

```

### app/api/smoke/tokenrouter/route.ts

```typescript
import { NextResponse } from "next/server";
import { callTokenRouter, tokenRouterModel } from "../../../../lib/tokenRouterClient";

export interface SmokeResult {
  status: "success" | "failed" | "skipped";
  model: string;
  durationMs?: number;
  reason?: string;
  httpStatus?: number;
  bodyPreview?: string;
  responsePreview?: string;
}

export async function GET(): Promise<NextResponse<SmokeResult>> {
  const model = tokenRouterModel();

  if (!process.env.TOKENROUTER_API_KEY) {
    return NextResponse.json({
      status: "skipped",
      model,
      reason: "TOKENROUTER_API_KEY not configured — add it to .env.local",
    });
  }

  const result = await callTokenRouter({
    messages: [{ role: "user", content: "Return only the word OK." }],
    timeoutMs: 15_000,
  });

  if (!result.ok) {
    return NextResponse.json({
      status: "failed",
      model,
      durationMs: result.durationMs,
      reason: result.reason,
      ...(result.httpStatus !== undefined ? { httpStatus: result.httpStatus } : {}),
      ...(result.bodyPreview ? { bodyPreview: result.bodyPreview } : {}),
    });
  }

  return NextResponse.json({
    status: "success",
    model: result.model,
    durationMs: result.durationMs,
    responsePreview: result.content.slice(0, 60),
  });
}

```

### postcss.config.js

```javascript
module.exports = {
  plugins: {
    tailwindcss: {},
    autoprefixer: {},
  },
};

```

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