Project Info
Inspiration
Accessibility tools are excellent at identifying issues, but they often leave teams with a long backlog and no proof that a proposed fix actually works for a real user. We wanted to close that gap. PRGate was inspired by a simple question: what if an accessibility agent could inspect a rendered website, fix the underlying code safely, look at the deployed result again, and only claim success when it has evidence? The goal is not to replace accessibility experts, but to remove repetitive remediation work while keeping human review and proof at the center.
What it does
PRGate audits a live public preview for accessibility barriers, including issues found by axe-core and visual issues that static analysis can miss. It then: Captures browser screenshots and accessibility-tree evidence. Prioritizes findings by user impact and WCAG relevance. Proposes source-level fixes inside an isolated GitHub branch. Runs the target repository’s tests. Waits for a Vercel Preview Deployment. Re-renders and re-audits the changed preview. Stores before and after evidence for every result. Opens a GitHub pull request only when all fixes in the batch verify. If a fix cannot be proven after the allowed retry attempts, PRGate marks it for human review instead of pretending it is complete.
How we built it
We built the dashboard and API layer with Next.js, React, and TypeScript. Supabase provides GitHub OAuth, persistent run history, private screenshot evidence, and rate limiting. Inngest orchestrates the durable audit, patch, preview, verification, daily rescan, and retention workflows. The browser worker runs in a Render Docker service with Playwright and axe-core, allowing the system to render real pages, capture screenshots, inspect accessibility information, and perform static audits. For safe remediation, PRGate uses Vercel Sandbox to clone the target repository into an isolated environment, create a disposable accessagent/run-* branch, apply bounded edits, run tests, and push the branch. Vercel then creates a Preview Deployment that PRGate audits again before opening a pull request through the GitHub API. Codex, using GPT-5.6 Terra during development, helped us implement, inspect, test, debug, and iterate on the system. The runtime uses the OpenAI Responses API through a structured provider interface for visual inspection, patch planning, and verification. Playwright remains responsible for browser control and evidence capture.
Challenges we ran into
The biggest challenge was making the system genuinely evidence-driven instead of producing a convincing but unverified demo. Browser automation on serverless infrastructure was difficult because full Chromium runtimes have storage, memory, and execution constraints. We separated the public Vercel dashboard from the Render-based browser worker to give Playwright a reliable Docker environment. We also encountered provider quotas, structured-output incompatibilities, sandbox patch failures, preview deployment timing, and Inngest endpoint synchronization conflicts. These failures shaped the product: patch edits are validated before application, tests run before preview verification, retries are bounded, and unsuccessful runs become human-review states. Another important challenge was avoiding false positives. For axe-originated issues, fresh axe results are authoritative after a re-render. For visual barriers, PRGate compares screenshots, accessibility context, and the original finding before calling a fix verified.
Accomplishments we're proud of
We are proud that PRGate completes a real closed loop instead of stopping at a report or generated diff. The system successfully: Audits a rendered target through Playwright and axe-core. Captures stored before and after browser evidence. Detects real missing image alternatives, unlabeled form controls, and contrast issues. Creates isolated source changes in temporary GitHub branches. Runs repository tests before deployment. Waits for and audits a real Vercel Preview Deployment. Verifies fixes using fresh browser-derived evidence. Creates a GitHub pull request only after all fixes verify. Keeps failed or inconclusive fixes out of the verified path. Supports scheduled daily rescans and evidence retention cleanup. We are also proud that the dashboard is designed around the same accessibility principles it evaluates: high contrast, clear states, visible evidence, keyboard-accessible controls, and no fabricated findings before a real audit occurs.
What we learned
We learned that autonomous coding is most valuable when it is paired with strict verification, not when it simply produces more code. A source diff does not prove that an accessibility issue is resolved. The deployed page may behave differently, a style change may create a regression, or an AI-generated edit may not apply cleanly. Real browser evidence, tests, isolated branches, and preview deployments make the system more trustworthy. We also learned that deployment architecture matters as much as prompts. Separating the lightweight dashboard from the browser-capable worker made the system more reliable and made its security boundaries clearer. Finally, we learned to treat model quotas and provider failures as normal operational states that need explicit retries, limits, and human-review paths.
What's next
for Access Agent Next, we want to expand keyboard-navigation and focus-management testing, improve source-file mapping for complex repositories, add team-level review workflows, and make the remediation policy more configurable. We also plan to add richer issue explanations for developers and accessibility consultants, stronger regression detection across multiple pages, configurable model tiers, cost controls, and deeper GitHub PR summaries. Long term, PRGate can become a continuous accessibility reliability layer: not just finding issues, but continuously proving that fixes remain valid as websites evolve.
Access-Agent
A closed-loop accessibility remediation agent. PRGate audits a rendered website, proposes source-level fixes in an isolated environment, re-renders the changed preview, and only opens a pull request when the recorded evidence verifies the fixes.
It is deliberately more than an accessibility scanner. A scanner can report a broken image alternative or unlabeled form field; AccessAgent carries that finding through a real browser audit, a disposable code branch, a test run, a deployment preview, and a second browser inspection before it makes a verification claim.
Team
- Mohammed Ayaan Adil Ahmed
- Elif İpek Aktaş
What is implemented
| Capability | What PRGate does |
|---|---|
| Rendered audit | Crawls an authorized same-origin site within page/depth limits, captures screenshots and an accessibility-tree snapshot, and runs axe-core. |
| Visual review | Sends screenshot, accessibility context, and static findings through a structured AI-provider contract to identify visual barriers such as insufficient contrast. |
| Safe source changes | Clones the configured repository into Vercel Sandbox, works only on a new PRGate/run-* branch, and never writes to main. |
| Test + preview gate | Runs the configured repository test command, pushes the temporary branch, then waits for its Vercel Preview Deployment. |
| Verification | Re-audits the preview, compares before/after screenshots, stores evidence, and labels each original finding as verified or needing review. |
| Pull request | Opens one GitHub PR only when every patch in the batch verifies; the branch includes the evidence files. |
| Durable workflows | Uses Inngest for retries, long-running orchestration, scheduled rescans, and retention cleanup. |
| Evidence dashboard | Shows a live trace, findings, diffs, screenshots, contrast chips, stored evidence, and scheduled-rescan state. |
The controlled /demo-target is intentionally inaccessible so that the full loop can be demonstrated safely. It is not an implementation example.
How Codex and GPT-5.6 are used
Codex + GPT-5.6 Terra: the development and verification environment
Codex, using GPT-5.6 Terra for this build, was used to turn the product specification into this working system: it inspected the repository, implemented the Next.js, Supabase, Inngest, browser-audit, sandbox, and GitHub integrations; ran type/unit checks; reviewed diffs; and iterated on deployment and runtime failures. That mirrors the core product thesis: make a source change, run real checks, and inspect the outcome rather than trusting an untested edit.
GPT-5.6: the intended high-capability reasoning and visual-review model
PRGate's runtime calls the OpenAI Responses API for three bounded tasks: visual accessibility inspection, source-edit proposal, and post-preview verification. When an OpenAI project provides a GPT-5.6 model identifier, set OPENAI_VISION_MODEL and OPENAI_PATCH_MODEL to that identifier to use it for those roles. Playwright—not the model—controls the browser, captures screenshots, and runs axe-core; the model receives the resulting evidence and must return schema-validated structured output.
The committed default is currently gpt-5-mini to keep a hackathon proof affordable. This is intentional model tiering, not a claim that every run used GPT-5.6. The workflow, evidence gates, sandbox, and verification behavior stay the same when the configured OpenAI model changes.
The idea in one minute
Broken deployed page
|
v
Playwright + axe-core + visual inspection find real barriers
|
v
AI patch role proposes minimal source edits in an isolated sandbox branch
|
v
Configured tests pass -> branch gets a Vercel Preview Deployment
|
v
Playwright re-renders and re-audits that preview
|
+--> any unresolved/regressed issue: retry (maximum 3) or needs human review
|
`--> every issue verified: evidence is committed and a GitHub PR is opened
The central product rule is simple: a diff is not proof. A finding is only marked verified after fresh browser-derived evidence exists for the deployed patch preview.
Architecture
flowchart LR
U[Developer in PRGate dashboard] --> V[Vercel: Next.js dashboard and run API]
V --> S[(Supabase: runs, findings, events, evidence)]
V --> E[Inngest event API]
E --> R[Render: Docker worker /api/inngest]
R --> B[Playwright + axe-core]
R --> A[AI provider: OpenAI production path]
R --> X[Vercel Sandbox: isolated clone and patch]
X --> G[GitHub temporary branch]
G --> P[Vercel Preview Deployment]
P --> B
R --> S
R --> G
Deployment responsibilities
| Service | Responsibility | Important boundary |
|---|---|---|
| Vercel | Public Next.js dashboard, GitHub sign-in, POST /api/runs, Vercel Preview Deployments, and Sandbox control plane. | It queues audit events; it does not run the durable browser/AI workflow. |
| Render | Docker worker with the Playwright-capable Chromium runtime and the Inngest function endpoint. | Holds AI, signing, patching, GitHub, and preview credentials. |
| Inngest | Durable workflow execution, retries, cron triggers, run timeline. | It must be synced to the Render endpoint, not the Vercel endpoint. |
| Supabase | GitHub OAuth session support, Postgres run history, Realtime dashboard data, private screenshot/evidence storage, and rate limiting. | Browser code uses only the publishable key; the secret key stays server-side. |
| GitHub | Repository source, short-lived patch branches, stored evidence, and final PR. | The fine-grained token needs Contents and Pull requests read/write access. |
Critical Inngest deployment rule
The Inngest Vercel integration must be disconnected for this project. That integration can automatically resync /api/inngest back to a Vercel deployment whenever Vercel deploys, replacing the Render worker endpoint. The intended active URL is:
https://<render-worker>.onrender.com/api/inngest
Vercel needs INNGEST_EVENT_KEY to send events. Render needs both INNGEST_EVENT_KEY and INNGEST_SIGNING_KEY because it serves and executes the signed workflows.
How the system works, bottom-up
1. User interface and API ingress
The frontend is Next.js 15 and React 19. It renders the run trace, evidence, findings, patch attempts, before/after pairs, and rescan controls. The design uses a high-contrast ink/paper palette and ratio chips because the product interface should meet the standard it asks other sites to meet.
When a user starts an audit, POST /api/runs validates that the target is an authorized, safe public URL; applies the per-hour audit limit; creates a queued Supabase run; and sends PRGate/audit.requested to Inngest. If ACCESSAGENT_REQUIRE_AUTH=true, GitHub OAuth through Supabase is required before the run can start.
This Vercel ingress route intentionally checks only its own needs: Supabase and the Inngest event key. It does not need an AI key or an Inngest signing key.
2. Durable multi-stage orchestration
Inngest receives the event and calls the worker endpoint hosted on Render. audit-patch-verify retains state across browser work, AI calls, sandbox work, preview deployment waits, retries, and errors. The stages are represented in the UI as:
- Crawl + audit
- Visual inspection
- Patch proposal
- Re-render
- Verification
The workflow retries provider-rate-limit errors and uses a hard maximum of three patch/preview/verification attempts. A run that cannot prove all fixes is never presented as successful and never opens a PR.
Two additional Inngest workflows exist:
scheduled-rescanruns daily at02:00UTC and queues due rescan records.retention-cleanupruns daily at02:30UTC and removes stale runs/evidence according toACCESSAGENT_RETENTION_DAYS.
3. Rendered browser audit
The worker launches Playwright Chromium against the target URL. It stays on the same origin and is bounded by configured page and depth caps. For each visited page it collects:
- full-page screenshot evidence;
- a compact accessibility-tree snapshot;
- axe-core baseline findings; and
- a small DOM fallback for common missing-alternative, label, button-name, and link-name cases when browser injection cannot produce a normal axe result.
The browser implementation uses the standard Playwright Chromium runtime on Render. Its Vercel-compatible path uses @sparticuz/chromium with constrained GPU options, but the Render Docker worker is the production browser executor because it has a full Playwright image.
4. Static and visual findings
@axe-core/playwright provides the deterministic rules baseline. The visual inspection role receives the screenshot, accessibility context, and static results through a strict JSON schema and validates the response with Zod. It augments—not replaces—the browser audit with visual barriers that static DOM rules may not catch, such as contrast problems.
Findings retain their WCAG reference, selector/context, impact, plain-language explanation, and evidence relationship. The workflow merges and prioritizes them by user impact before attempting source changes.
5. Provider abstraction
The runtime has one structured provider interface for visual inspection, verification, and patch edits.
- OpenAI is the production default. It uses the OpenAI Responses API through the
openaiSDK. - Gemini is an isolated test adapter. It uses Google GenAI Interactions with
store: falseand the same Zod-validated output shape.
The provider never receives authority to write directly into the repository. Its patch output is a structured list of path, oldText, and newText edits, which the sandbox validates before applying.
6. Isolated patching and tests
The patch stage starts a Vercel Sandbox, clones ACCESSAGENT_REPO_URL, and creates a fresh branch named like:
PRGate/run-<timestamp>-attempt-<n>
It inventories the relevant project files, asks the patch role for bounded edits, then accepts an edit only when it targets a permitted relative file and its old text matches once. It also runs git diff --check and the human-configured ACCESSAGENT_TEST_COMMAND before it commits and pushes.
For the controlled high-frequency findings, there is a narrowly scoped deterministic fallback: add a missing alt, add a form control label/aria-label, or strengthen a known low-contrast literal. That fallback runs only after model edits fail and only inside source files in the clone; it still goes through the same test, preview, and verification gates.
7. Preview and verification
Once the temporary branch is pushed, PRGate asks Vercel for the branch preview and waits for it to become ready. It then repeats the browser audit against that preview.
Verification does not trust a model response alone:
- a fresh axe result is authoritative for axe-originated issues; and
- the visual verifier compares the before screenshot, after screenshot, accessibility-tree evidence, and target finding for visual issues.
If an original issue has disappeared from the fresh relevant audit and no regression is found, it is marked Verified. Otherwise it is reviewed again in the next bounded attempt or remains Review / needs human review.
8. Evidence and pull request
Supabase persists runs, findings, patch attempts, run events, rescan records, and screenshot references. When all fixes verify and ACCESSAGENT_PUBLISH_PR_EVIDENCE=true, the PR stage commits the before/after evidence into .PRGate/evidence/<run-id>/ on the temporary branch and opens one GitHub PR to GITHUB_BASE_BRANCH (normally main).
PRGate never pushes to main, never auto-merges, and never opens a PR containing unverified findings.
Technology stack
| Area | Technologies |
|---|---|
| Frontend/API | Next.js 15, React 19, TypeScript |
| Durable agent workflow | Inngest |
| Browser audit | Playwright, @axe-core/playwright, axe-core, @sparticuz/chromium for constrained serverless compatibility |
| AI | OpenAI Responses API (openai SDK) by default; optional Google GenAI adapter for isolated testing |
| Data and auth | Supabase Postgres, Storage, Realtime, SSR auth, GitHub OAuth |
| Sandboxed code execution | Vercel Sandbox |
| Preview deployment lookup | Vercel API |
| Git + PR automation | Octokit / GitHub REST API |
| Validation | Zod, TypeScript type checking, Node test runner, Playwright e2e support |
| Worker runtime | Docker image based on Microsoft Playwright, deployed to Render |
Local development
Prerequisites
- Node.js 20+ and npm
- Supabase project with migration applied
- Inngest account/app
- GitHub OAuth configured in Supabase (if sign-in is enabled)
- Vercel project connected to the target GitHub repository
- Render Docker Web Service for the worker
Run it
copy .env.example .env.local
npm install
npm run dev
Open http://localhost:3000. The live, production-equivalent path requires the service configuration described below; the dashboard itself can be explored without starting an audit.
Validate the repository
npm run test
npm run test:e2e
npm run build
npm run test performs TypeScript checking plus unit tests. Browser e2e tests require the relevant local browser dependencies.
Configuration and deployment
Copy .env.example to .env.local for local work. Never commit .env.local, provider keys, GitHub tokens, Supabase secret keys, or Vercel tokens.
Required on Vercel (dashboard/ingress)
| Variable | Purpose |
|---|---|
NEXT_PUBLIC_APP_URL | Public dashboard URL. |
NEXT_PUBLIC_SUPABASE_URL | Supabase project URL. |
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY | Browser-safe Supabase key. |
SUPABASE_SECRET_KEY | Server-side Supabase access for the ingress/data path. |
INNGEST_EVENT_KEY | Sends audit events to Inngest. |
ACCESSAGENT_REQUIRE_AUTH | Set to true to require GitHub sign-in. |
| optional limits | ACCESSAGENT_MAX_PAGES, ACCESSAGENT_MAX_DEPTH, ACCESSAGENT_AUDITS_PER_HOUR. |
Do not put INNGEST_SIGNING_KEY, OPENAI_API_KEY, GEMINI_API_KEY, GitHub token, or Vercel Sandbox credentials on Vercel for this split architecture. Those belong to the worker.
Required on Render (worker)
Render uses the committed Dockerfile and render.yaml as the worker definition. Set the health check to /api/health, and set these values in the Render service:
| Group | Variables |
|---|---|
| Inngest | INNGEST_EVENT_KEY, INNGEST_SIGNING_KEY |
| Supabase | NEXT_PUBLIC_SUPABASE_URL, NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY, SUPABASE_SECRET_KEY |
| AI | ACCESSAGENT_AI_PROVIDER, provider API key, provider model values |
| Repository/PR | ACCESSAGENT_REPO_URL, ACCESSAGENT_TEST_COMMAND, ACCESSAGENT_PUBLISH_PR_EVIDENCE, GITHUB_TOKEN, GITHUB_OWNER, GITHUB_REPO, GITHUB_BASE_BRANCH |
| Sandbox + preview | VERCEL_TOKEN, VERCEL_PROJECT_ID, VERCEL_TEAM_ID |
| Limits/operations | ACCESSAGENT_MAX_PAGES, ACCESSAGENT_MAX_DEPTH, ACCESSAGENT_MAX_ATTEMPTS, ACCESSAGENT_RETENTION_DAYS, optional ACCESSAGENT_ALERT_WEBHOOK_URL |
For a target repository, use a test command that starts from a clean clone, for example:
npm ci --legacy-peer-deps && npm run test
Supabase setup
- Run
supabase/migrations/001_initial.sqlin the Supabase SQL editor. Choose Run and enable RLS if prompted. - Configure GitHub as an OAuth provider in Supabase Auth if
ACCESSAGENT_REQUIRE_AUTH=true. - Add both local and deployed
/auth/callbackredirect URLs to Supabase. - Use a publishable key in
NEXT_PUBLIC_SUPABASE_PUBLISHABLE_KEY; keepSUPABASE_SECRET_KEYserver-only.
GitHub setup
Create a fine-grained GitHub token scoped to the target repository with:
- Contents: Read and write
- Pull requests: Read and write
Set ACCESSAGENT_REPO_URL to the clone URL of that repository. PRGate creates temporary PRGate/run-* branches, pushes them, and opens PRs to GITHUB_BASE_BRANCH. After reviewing the result, keep the branch backing an open PR and delete stale failed/abandoned PRGate/run-* branches from GitHub.
Inngest setup
-
Put the event and signing keys into Render; put only the event key into Vercel.
-
In Inngest, manually sync the active app to the Render endpoint:
https://<render-worker>.onrender.com/api/inngest -
Confirm Inngest lists three functions: audit/patch/verify, scheduled rescan, and retention cleanup.
-
Do not enable the Vercel Inngest integration for this project; it will overwrite the active Render sync after Vercel deployments.
Provider selection
Production: OpenAI
Set these on Render:
ACCESSAGENT_AI_PROVIDER=openai
OPENAI_API_KEY=your_project_api_key
OPENAI_VISION_MODEL=gpt-5-mini
OPENAI_PATCH_MODEL=gpt-5-mini
ACCESSAGENT_AI_PROVIDER defaults to openai, so the explicit value is useful for clarity but not required. After changing Render environment values, redeploy the Render worker. Keep the Inngest app synchronized to Render.
Isolated test provider: Gemini
Gemini is available only as an alternative testing adapter:
ACCESSAGENT_AI_PROVIDER=gemini
GEMINI_API_KEY=...
GEMINI_VISION_MODEL=...
GEMINI_PATCH_MODEL=...
For a bounded proof run, use ACCESSAGENT_MAX_PAGES=1 and ACCESSAGENT_MAX_DEPTH=0. Switch back to openai for the intended production configuration. The inactive provider key is not used.
Safety and operating constraints
- Audit only sites you own or have explicit authorization to test.
- URL checks reject unsafe/local/private targets before an audit is queued.
- Browser crawling remains same-origin and is capped by page/depth limits.
- Generated edits run in a disposable sandbox clone and a new branch; the primary checkout and
mainare never patch targets. - A repository test command and preview deployment are mandatory gates before verification.
- Evidence is private in Supabase and is attached to the patch branch only when PR evidence publishing is explicitly enabled.
- No unresolved or inconclusive issue is called verified. The result is a review state, not a false success.
Troubleshooting
| Symptom | Likely cause and correction |
|---|---|
| Inngest changes back to a Vercel URL | Disconnect the Vercel Inngest integration, then resync Inngest manually to the Render /api/inngest URL. |
Live audits are unavailable ... INNGEST_SIGNING_KEY on Vercel | The Vercel ingress should not require it. Ensure the current code is deployed and set the signing key on Render instead. |
401 Event key not found | Use the event key from the same Inngest environment on both Vercel and Render, then redeploy the affected service. |
| Browser executable/Chromium errors on Vercel | The durable workflow must run from the Render Docker worker, not from Vercel. |
| Sandbox rejects a patch | This is a safe failure: the edit did not meet exact-match, diff, or test gates. The workflow retries within its attempt limit and otherwise marks review. |
| Patch preview does not appear | Confirm the target repository is connected to Vercel and the Render worker has a valid VERCEL_TOKEN, VERCEL_PROJECT_ID, and VERCEL_TEAM_ID. |
| OpenAI 429/quota error | The API project needs active billing/available credits and must be within its model rate limits; creating another key inside the same unfunded project does not add quota. |
Scope and roadmap
PRGate is designed for web accessibility remediation and does not claim legal certification or complete WCAG AAA coverage. The next practical improvements are broader interactive keyboard/focus testing, richer issue-to-source mapping, team/organization controls, configurable policy gates, and more production observability/cost controls.
License
No license has been declared for this repository yet.
Analysis
View
Metric
- 37
- 6
Figures cover GitHub contributors during the hackathon window. A co-authored commit counts in full for each author, so per-member totals add up to more than the whole-team figures.
Technology
- CSSIn code
- Next.jsIn code
- OpenAIIn code
- ReactIn code
- SQLIn code
- SupabaseIn code
- TypeScriptIn code
- DockerClaimed
- VercelClaimed
7 of 9 appear in the indexed code. 2 claimed on Devpost could not be matched to code, which may simply mean the tool leaves no trace in the repository.
AI coding agents
No AI coding agent signals were found in this repository.
Detected from committed agent config files and commit authorship. Absence of a signal is not proof an agent was unused.
Codebase size
Source size
143 KB
Source files
46
Counts recognized source files only; vendored directories, binaries and lockfiles are excluded, so this is smaller than the repository on disk.
Repository
git791/Access-Agent
56 files · 373 KB · @ 83889ee
Structure
Interface
11 files · 20%Screens, components and styles rendered to the user.
API & routing
6 files · 11%Request entry points: routes, handlers and controllers.
Application logic
18 files · 32%Domain rules, services and shared utilities.
Data & schema
1 file · 2%Schema definitions, migrations and data access.
Supporting
Layers are inferred from where files sit in the tree, not from reading the code. A project that names its directories unconventionally will read oddly here — open the file browser to check anything the diagram implies.
Languages
- TypeScript70%
- Markdown15%
- CSS11%
- SQL3%
- YAML1%
Share of indexed source by file size. Binary and vendored files are excluded.
Dependencies
package.json
npm · 21- @axe-core/playwright
- @google/genai
- @playwright/test
- @sparticuz/chromium
- @supabase/ssr
- @supabase/supabase-js
- @vercel/sandbox
- axe-core
- inngest
- next
- octokit
- openai
- playwright-core
- react
- react-dom
- zod
- +5 more
Declared in the repository’s manifests at the indexed commit. A declared package is not proof it is used, and runtime dependencies are listed first.
This project’s features have not been analysed yet.
Export this project's context (description, README, evidence, key source files) to chat with an AI agent elsewhere.