# Project export: Repair Console: Approval-Gated Playwright Locator Repair

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: OpenAI Build Week
- Tagline: Repair Console turns a broken Playwright locator into a reviewable repair: it captures failure evidence, proposes one selector change, requires approval, then verifies the target test and full suite.
- Devpost: https://devpost.com/software/tbd-q24caj
- GitHub: https://github.com/peishuen/OpenAI_BuildWeek
- Video: https://www.youtube.com/embed/MCH7RnR9lug?enablejsapi=1&hl=en_US&rel=0&start=&version=3&wmode=transparent
- Team: 3 GitHub contributor(s) — lokolokk (19 commits), oooi0001 (9 commits), ywon0112 (1 commits)

## Devpost submission (written by the team)

### Inspiration

Playwright tests often fail for a simple reason: the product UI changes, but a locator in the test still points to an old selector. Fixing that is usually small work, yet it interrupts developers and can become risky when automated tools are allowed to modify code without review. We wanted to explore a more trustworthy form of AI assistance: one that helps developers understand and repair a narrow failure, while keeping the human in control.

### What it does

Repair Console is an approval-gated Playwright locator repair tool. In its bundled browser sandbox, a user can simulate a login-button selector regression without a terminal command. The console captures the failed selector, a short error message, the source location, and a sanitized DOM snapshot. Qwen proposes one replacement CSS selector with concise evidence. The user reviews the exact diff and must select Approve & rerun before any test file changes. After approval, Repair Console applies one validated selector change, reruns the affected test, and then verifies the full demo suite.

### How we built it

We built the project with React, Vite, Express, TypeScript, Playwright, Vitest, Zod, and a server-only Qwen integration. Codex and GPT-5.6 helped us refine the idea into a deliberately narrow product scope, write the specification and task plan, implement the approval-gated workflow, create tests, and improve the browser demo experience. The backend validates every proposal and restricts writes to one CSS-selector literal in the Playwright test directory. Server-sent events keep the dashboard timeline updated while the repair is being verified.

### Challenges we ran into

The main challenge was making the demo feel like a real customer workflow without making unsafe claims about autonomous code repair. We replaced terminal-driven mutation steps with visible browser controls, while keeping the mutation limited to the bundled sandbox fixture. We also found and corrected a baseline-state mismatch between the fixture, test selector, and sandbox labels. This reinforced why deterministic browser and mutation tests are essential for a live demo. Another challenge was balancing live AI behavior with demo reliability. Qwen is the primary provider when configured, while a clearly labelled offline fixture fallback preserves the same approval and verification safety model for rehearsal.

### Accomplishments we're proud of

We created a complete browser-operated repair loop: simulate a regression, inspect evidence, review one diff, approve the patch, and watch both the target test and full suite pass. We are especially proud that the system does not patch before approval, does not expose credentials to the browser, and restores the test file if verification fails. The product makes its limits visible instead of presenting itself as a general autonomous test-healing system.

### What we learned

AI-assisted developer tools become more credible when their scope is narrow, their evidence is visible, and their actions are reversible. We learned that the most valuable automation is not necessarily fully autonomous. A small, fast, reviewable repair loop can reduce maintenance work while preserving developer judgment and trust.

### What's next

Next, we would validate the workflow with real frontend and QA engineers, improve selector confidence signals, and support more controlled locator patterns. Longer term, we would explore secure repository connections, isolated workers, persistent audit history, and CI integrations. Those additions would require strong authentication, repository isolation, and policy controls before the product could responsibly move beyond its local sandbox.

## README (from the GitHub repository)

# Repair Console

Repair Console is an approval-gated developer tool for one common Playwright maintenance problem: a frontend selector changes while an end-to-end test still expects the old selector.

Instead of applying a model-generated change automatically, the console captures focused failure evidence, presents one proposed CSS-selector replacement, and requires explicit developer approval. After approval, it verifies the repaired target test and the complete demo suite.

**Track:** Developer Tools

**Supported platform:** local Windows development with Node.js 22 LTS. The project uses a browser-based sandbox and does not require a database, external repository connection, or CI account.

## Demo video

[Watch the demo on YouTube](https://youtu.be/MCH7RnR9lug)

The video demonstrates the browser-only workflow: simulate a selector regression, review the proposal, approve the change, and watch the target test and full suite pass.

## What it does

- Simulates a controlled login-button selector regression directly in the browser.
- Captures the failed selector, short error message, source location, and sanitized DOM snapshot.
- Uses Qwen as the live proposal provider when server-side configuration is available.
- Shows the diagnosis, evidence, and exact selector diff before any test file changes.
- Permits exactly one validated CSS-selector literal change below `tests/e2e/`.
- Requires **Approve & rerun** before patching, then verifies the target test and full suite.
- Provides a clearly labelled offline fixture fallback for deterministic rehearsal.

## Quick start

### Prerequisites

- Node.js 22 LTS
- npm
- Chromium for Playwright

From a fresh clone:

```powershell
cd code
npm ci
npx playwright install chromium
Copy-Item .env.example .env
npm run dev
```

Open [http://127.0.0.1:5173](http://127.0.0.1:5173). The React dashboard runs on port `5173`; its local Express API runs on port `3001`.

### Live Qwen configuration

Edit `code/.env` locally; never commit it.

```dotenv
QWEN_API_KEY=your-server-only-key
QWEN_BASE_URL=https://your-qwen-openai-compatible-endpoint/v1
QWEN_MODEL=qwen3.7-plus-2026-05-26
```

When both the key and base URL are configured, **Live Qwen** is preselected in the console. Otherwise, select **Offline fixture fallback** to exercise the same approval and verification workflow without a network call.

## Judge-friendly sandbox

Judges can test the complete approval-gated workflow without Qwen credentials:

1. Follow the Quick start steps.
2. Open the local dashboard and select **Offline fixture fallback**.
3. Follow the Browser demo flow below.

The fallback changes no safety policy: it still requires approval, patches one validated selector literal, and runs the target test and full suite.

## Browser demo flow

1. Start at **Fixture state: Baseline**.
2. Select **Simulate selector regression**. The login fixture changes but the Playwright test remains stale.
3. Select **Start repair**, review the failure, evidence, and one-selector diff.
4. Select **Approve & rerun**. Do not refresh or reset while verification is in progress.
5. Wait for **Repair completed**: the patched target test and the full suite have passed.

After a successful repair, use **Simulate selector regression** again to start the next controlled cycle. **Reset sandbox** is for recovery before approval or after a failed repair; it is deliberately unavailable after a successful repair.

## Commands

Run these from `code/`.

| Command | Purpose |
| --- | --- |
| `npm run dev` | Start the Vite dashboard and Express API. |
| `npm run lint` | Run ESLint. |
| `npm run typecheck` | Run TypeScript strict-mode checking. |
| `npm run test:unit` | Run the Vitest unit and API tests. |
| `npm run test:e2e` | Run the Playwright browser suite. |
| `npm run test:mutation` | Verify that the controlled mutation causes exactly the `@repair-target` test to fail, then restores the prior state. |
| `npm run build` | Type-check and create a production frontend build. |

Run the complete quality gate before a demo or submission:

```powershell
npm run lint
npm run typecheck
npm run test:unit
npm run test:e2e
npm run build
```

## Safety model

The scope is intentionally narrow:

- The browser can mutate only the bundled login button ID; it cannot send file paths, shell commands, or arbitrary source changes.
- Qwen credentials stay server-side. The browser never receives API keys, base URLs, or raw provider errors.
- A proposal is data, not code. It must pass schema validation and the locator-only patch policy.
- No test file changes before explicit approval.
- Failed target or full-suite verification restores the pre-patch test file.

Repair Console is a local sandbox demonstration, not a hosted service for arbitrary repositories, CI providers, or general-purpose autonomous test healing.

## Architecture

```text
React/Vite dashboard
  -> Express repair API and SSE status updates
  -> Playwright failure capture and sanitized DOM context
  -> Qwen or recorded fixture proposal
  -> strict proposal validation and one-string patch policy
  -> explicit approval
  -> target-test verification -> full-suite verification
```

Key implementation areas:

- `code/src/RepairConsole.tsx` — browser workspace, provider selection, proposal review, and timeline.
- `code/src/repair-orchestrator.ts` — approval-gated repair lifecycle and restoration behavior.
- `code/src/qwen-proposal-provider.ts` — server-only Qwen JSON proposal provider.
- `code/src/sandbox-fixture.ts` — fixed, browser-triggered login-selector mutation boundary.
- `code/tests/` — Vitest unit/API coverage and Playwright browser coverage.

## How Codex and GPT-5.6 were used

Codex and GPT-5.6 accelerated the project from idea to verified demo:

- Refined the product from a broad “self-healing tests” concept into one constrained, reviewable locator-repair workflow.
- Produced the specification, dependency-ordered implementation plan, safety boundaries, and demo plan.
- Implemented the React/Express workflow incrementally with strict TypeScript, validation, and approval-gated patching.
- Created and iterated on unit, API, mutation, and browser tests.
- Reviewed the browser workflow, identified a sandbox baseline inconsistency, added its regression coverage, and verified the corrected end-to-end flow.
- Prepared the demo narrative and submission documentation.

The core product specification is in [docs/specs/self-healing-playwright-repair-console.md](docs/specs/self-healing-playwright-repair-console.md). The browser-operated sandbox extension and its safety boundary are documented in [docs/specs/browser-only-sandbox-repair-workflow.md](docs/specs/browser-only-sandbox-repair-workflow.md).

## Scope

**In scope:** one Playwright CSS selector repair, a server-only live proposal provider, a deterministic fallback, explicit approval, and local target/full-suite verification.

**Out of scope:** application-code repairs, multi-file patches, automatic approval, authentication, databases, hosted repository connections, CI integration, commits, and pull requests.


## Detected evidence (automated analysis)

Indexed codebase: 54 recognized source files, 223 KB.
- CSS (language) — detected in the code
- Express (technology) — detected in the code
- HTML (language) — detected in the code
- JavaScript (language) — detected in the code
- OpenAI (technology) — detected in the code
- React (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: Codex — evidence: config files committed to the repository

## Codebase structure (from repository index)

### Files (60 of 60)

```
.gitignore
code/.env.example
code/.gitignore
code/AGENTS.md
code/eslint.config.js
code/index.html
code/package.json
code/playwright.config.ts
code/scripts/repair-mutation.mjs
code/src/App.tsx
code/src/env.ts
code/src/failure-context.ts
code/src/fixture-proposal-provider.ts
code/src/LoginPage.tsx
code/src/main.tsx
code/src/playwright-test-runner.ts
code/src/proposal-provider.ts
code/src/proposal-validator.ts
code/src/qwen-proposal-provider.ts
code/src/repair-client.ts
code/src/repair-console-status.ts
code/src/repair-console.css
code/src/repair-events.ts
code/src/repair-orchestrator.ts
code/src/repair-routes.ts
code/src/repair.ts
code/src/RepairConsole.tsx
code/src/sandbox-fixture.ts
code/src/server.ts
code/src/styles.css
code/src/test-patcher.ts
code/tests/e2e/login.spec.ts
code/tests/fixtures/playwright-reports.ts
code/tests/fixtures/repair-proposals.ts
code/tests/unit/env.test.ts
code/tests/unit/fixture-proposal-provider.test.ts
code/tests/unit/playwright-test-runner.test.ts
code/tests/unit/qwen-proposal-provider.test.ts
code/tests/unit/repair-api.test.ts
code/tests/unit/repair-client.test.ts
code/tests/unit/repair-console.test.ts
code/tests/unit/repair-events.test.ts
code/tests/unit/repair-orchestrator.test.ts
code/tests/unit/repair-patcher.test.ts
code/tests/unit/repair.test.ts
code/tests/unit/sandbox-fixture.test.ts
code/tests/unit/server.test.ts
code/tsconfig.json
code/vite.config.ts
code/vitest.config.ts
docs/demo-script.md
docs/ideas/self-healing-playwright-repair-console.md
docs/phase-1-sources.md
docs/specs/browser-only-sandbox-repair-workflow.md
docs/specs/self-healing-playwright-repair-console.md
hackathon_ideas.md
README.md
tasks/plan.md
tasks/task-12-skill-activation.md
tasks/todo.md
```

### Dependencies

- code/package.json: @eslint/js@^9.33.0, @playwright/test@^1.55.0, @types/express@^5.0.3, @types/jsdom@^28.0.3, @types/node@^24.3.0, @types/react@^19.1.10, @types/react-dom@^19.1.7, @vitejs/plugin-react@^4.4.1, concurrently@^9.2.1, dotenv@^16.6.1, eslint@^9.33.0, eslint-plugin-react-hooks@^5.2.0, eslint-plugin-react-refresh@^0.4.20, express@^5.1.0, globals@^16.3.0, jsdom@^26.1.0, openai@^6.48.0, react@^19.1.1, react-dom@^19.1.1, tsx@^4.20.5, typescript@~5.9.2, typescript-eslint@^8.39.0, vite@^6.1.0, vitest@^3.2.4, zod@^4.4.3

### Recent commits (newest first)

- updated readme file
- added readme file
- Restore canonical sandbox baseline and strengthen regression coverage
- completed Task 12
- Define browser-only sandbox and Qwen-primary repair workflow
- fix: show repair outcome after approval
- modified workflow
- docs: record the Qwen integration
- refactor: replace OpenAI provider with Qwen
- feat: configure Qwen live proposals
- feat: add Qwen proposal adapter
- fix error
- fix bug
- completed Task 11
- completed Task 10
- completed dashboard
- exposed repair-run API and event stream
- fix: run Playwright repair checks without a Windows shell
- feat: add safe fixture-driven Playwright repair orchestration
- completed task 4 and task 5

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

### hackathon_ideas.md

```markdown
# Project Vision: Self-Healing E2E Test Agent (Developer Tools Track)

## The Problem
End-to-End (E2E) UI tests are notoriously brittle. A frontend developer changes a simple button's ID from `#submit` to `#checkout`, or slightly restructures a `div`, and suddenly the entire CI/CD pipeline crashes. QA engineers and developers spend up to 30% of their time just maintaining and updating broken test selectors. This slows down release cycles, frustrates teams, and makes companies hesitant to write E2E tests in the first place.

## The Solution
A "Self-Healing" test agent powered by Codex and GPT-5.6. When a UI test fails, the agent intercepts the failure in real-time. It analyzes the DOM state at the exact moment of failure, compares it to the intent of the test, deduces what changed (e.g., "The submit button is now `#checkout`"), and autonomously rewrites the test code to fix it. 

## Value Proposition
- **Zero Maintenance:** Reduces test maintenance time to practically zero.
- **Unblocked Pipelines:** Keeps CI/CD pipelines moving without waiting for human intervention to fix trivial UI updates.
- **Developer Experience:** Lets developers focus on building features, not fixing brittle tests.

## The Competitive Moat (Why not just use ChatGPT?)
When pitching, judges will inevitably ask: *"Why can't a developer just copy and paste the failing test into Claude/ChatGPT?"* This project solves the massive friction in that manual workflow:
1. **The "State" Problem:** A failing E2E test in a CI/CD pipeline doesn't just need the test code; it needs the exact DOM state at the millisecond of failure. A developer would have to run the test locally, wait for it to fail, open DevTools, and manually copy the HTML. Our agent hooks directly into the test runner to capture this state automatically, eliminating a 5-minute context-gathering chore.
2. **The Token & Noise Problem:** Copy-pasting raw modern HTML (with inline SVGs, massive CSS classes, and scripts) wastes tokens and causes LLM hallucinations. Our agent programmatically sanitizes the DOM before the LLM ever sees it.
3. **The Auto-Verification Loop:** A manual ChatGPT user has to copy the AI's fix, paste it into their IDE, and re-run the test to verify it works. Our agent autonomously writes the fix to the file, re-runs the test, and verifies success instantly. 

*(Pitch Summary: "ChatGPT is great when you have the context. But in E2E testing, gathering the context takes 90% of the time. Our agent gathers the context, cleans it, writes the fix, and verifies it. We turn a 10-minute manual chore into a 10-second background task.")*

## The Competitive Moat Part 2 (Why not just use generic agents like Claude Computer Use?)
Judges might ask: *"Why not just use an agent with 'Computer Use' or browser-control to run the tests visually?"* 
1. **Speed & Scalability (The CI/CD Nightmare):** Generic visual agents take screenshots, move cursors, and evaluate pixel changes step-by-step. They take minutes per test. If a 
[truncated — 3543 more characters]
```

### docs/phase-1-sources.md

```markdown
# Phase 1 implementation sources

The project uses the installed versions in `code/package.json`; these official references record the framework-specific decisions made in Phase 1.

- [Vitest v3 configuration](https://v3.vitest.dev/config/#include): `vitest.config.ts` uses the documented `test.include` glob to collect only unit tests and avoid collecting Playwright specs.
- [Playwright Test configuration](https://playwright.dev/docs/test-configuration): `testDir`, `webServer`, `baseURL`, and the bounded per-test timeout are configured through Playwright's top-level configuration options.
- [Vite server options](https://vite.dev/config/server-options#server-proxy): Vite owns the local frontend server and `/api` proxy configuration.
- [React `useState`](https://react.dev/reference/react/useState): the login demo keeps submission feedback as component-local state.

```

### code/package.json

```
{
  "name": "self-healing-playwright-repair-console",
  "description": "Define the project packages and commands used to run the React frontend, Express server, and automated checks.",
  "private": true,
  "version": "0.0.1",
  "type": "module",
  "scripts": {
    "dev": "concurrently -k \"vite\" \"tsx watch src/server.ts\"",
    "build": "tsc --noEmit && vite build",
    "lint": "eslint .",
    "typecheck": "tsc --noEmit",
    "test:unit": "vitest run",
    "test:e2e": "playwright test",
    "mutation:apply": "tsx scripts/repair-mutation.mjs apply",
    "mutation:reset": "tsx scripts/repair-mutation.mjs reset",
    "test:mutation": "tsx scripts/repair-mutation.mjs verify"
  },
  "dependencies": {
    "dotenv": "^16.6.1",
    "express": "^5.1.0",
    "jsdom": "^26.1.0",
    "openai": "^6.48.0",
    "react": "^19.1.1",
    "react-dom": "^19.1.1",
    "zod": "^4.4.3"
  },
  "devDependencies": {
    "@eslint/js": "^9.33.0",
    "@playwright/test": "^1.55.0",
    "@types/express": "^5.0.3",
    "@types/jsdom": "^28.0.3",
    "@types/node": "^24.3.0",
    "@types/react": "^19.1.10",
    "@types/react-dom": "^19.1.7",
    "@vitejs/plugin-react": "^4.4.1",
    "concurrently": "^9.2.1",
    "eslint": "^9.33.0",
    "eslint-plugin-react-hooks": "^5.2.0",
    "eslint-plugin-react-refresh": "^0.4.20",
    "globals": "^16.3.0",
    "tsx": "^4.20.5",
    "typescript": "~5.9.2",
    "typescript-eslint": "^8.39.0",
    "vite": "^6.1.0",
    "vitest": "^3.2.4"
  }
}

```

### code/src/main.tsx

```typescript
/*
  Start the React application and place it in the <div id="root"> from index.html.
*/
import { StrictMode } from "react";
import { createRoot } from "react-dom/client";
import App from "./App";
import "./styles.css";
import "./repair-console.css";

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

```

### code/src/App.tsx

```typescript
/*
  Display the controlled login demo used by the Playwright tests.
*/
import LoginPage from "./LoginPage";
import RepairConsole from "./RepairConsole";

export default function App() {
  const isLoginPage = window.location.pathname === "/login";

  return (
    <div className="app-shell">
      {isLoginPage ? <LoginPage /> : <RepairConsole />}
    </div>
  );
}

```

### code/src/server.ts

```typescript
/*
  Run the Express backend for this project
*/
import "dotenv/config";
import { dirname, resolve } from "node:path";
import { fileURLToPath } from "node:url";
import express from "express";

import { getPublicProviderAvailability, readRepairEnvironment } from "./env";
import { FixtureProposalProvider, recordedFailureDomSnapshot } from "./fixture-proposal-provider";
import { QwenProposalProvider } from "./qwen-proposal-provider";
import { RepairEventStore } from "./repair-events";
import { RepairOrchestrator } from "./repair-orchestrator";
import {
  apiErrorHandler,
  createRepairRouter,
  type ProviderAvailability,
  type RepairRunController,
  type SandboxController,
} from "./repair-routes";
import { getSandboxFixtureState, resetSandboxFixture, toggleSandboxFixture } from "./sandbox-fixture";
import { NodePlaywrightTestRunner } from "./playwright-test-runner";

// Return the service status for health checks
export function getHealthStatus() {
  return { ok: true };
}

export function createApp(
  controller: RepairRunController,
  events: RepairEventStore,
  sandbox: SandboxController,
  providers: ProviderAvailability,
) {
  const app = express();
  app.use(express.json());

  // Respond to a simple health-check request.
  app.get("/api/health", (_request, response) => {
    response.json(getHealthStatus());
  });

  app.use("/api", createRepairRouter(controller, events, sandbox, providers));
  // Return safe API errors after every API route has had a chance to handle requests.
  app.use("/api", apiErrorHandler);
  return app;
}

const port = Number(process.env.PORT ?? 3001);
const projectRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..");
const events = new RepairEventStore();
const environment = readRepairEnvironment();
const proposalProviders = {
  qwen: new QwenProposalProvider({
    apiKey: environment.QWEN_API_KEY,
    baseURL: environment.QWEN_BASE_URL,
    model: environment.QWEN_MODEL,
  }),
  fixture: new FixtureProposalProvider(),
};
const sandbox: SandboxController = {
  getState: getSandboxFixtureState,
  simulate: toggleSandboxFixture,
  reset: resetSandboxFixture,
};
// Connect repair progress updates to the in-memory SSE event store.
const orchestrator = new RepairOrchestrator({
  projectRoot,
  runner: new NodePlaywrightTestRunner({ projectRoot }),
  proposalProviders,
  defaultProposalMode: environment.REPAIR_PROPOSAL_PROVIDER,
  recordedDomSnapshot: recordedFailureDomSnapshot,
  onRunUpdate: (run) => events.publish(run),
});
export const app = createApp(orchestrator, events, sandbox, getPublicProviderAvailability(environment));

// Start the server outside the unit-test process
if (!process.env.VITEST) {
  app.listen(port, () => {
    console.log(`Server listening at http://localhost:${port}`);
  });
}

```

### code/vitest.config.ts

```typescript
// Source: https://vitest.dev/config/#include
// Keep fast unit tests separate from Playwright's browser test files.
import { defineConfig } from "vitest/config";

export default defineConfig({
  test: {
    include: ["tests/unit/**/*.test.ts"],
  },
});

```

### code/vite.config.ts

```typescript
/*
  Configure Vite, the tool that runs and builds the React frontend.
  Forward future /api requests to the local Express server instead of treating them as frontend routes.
*/
import { defineConfig } from "vite";
import react from "@vitejs/plugin-react";

export default defineConfig({
  plugins: [react()],
  server: {
    host: "127.0.0.1",
    proxy: {
      "/api": "http://localhost:3001",
    },
  },
});

```

### code/index.html

```html
<!doctype html>
<!--
  Load the basic browser page for the React app.
  React uses the "root" element below as the place to display App.tsx.
-->
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Repair Console</title>
  </head>
  <body>
    <div id="root"></div>
    <script type="module" src="/src/main.tsx"></script>
  </body>
</html>

```

### code/playwright.config.ts

```typescript
/*
  Configure Playwright to start the local app before browser tests run
  Source: https://playwright.dev/docs/test-configuration
*/
import { defineConfig } from "@playwright/test";

export default defineConfig({
  testDir: "./tests/e2e",
  timeout: 10_000,
  use: {
    baseURL: "http://127.0.0.1:5173",
  },
  webServer: {
    command: "npm run dev",
    // Wait for Express as well as Vite so dashboard tests never call /api before it is ready
    url: "http://127.0.0.1:3001/api/health",
    reuseExistingServer: !process.env.CI,
  }
})

```

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