The problem
Our Ability is a job platform built for people with disabilities. Employers send PDFs, benefit guides, HR forms, legal documents. A lot of those PDFs are unreadable by screen readers. Someone opens a document meant to help them apply for a job, and JAWS or NVDA gives back nothing useful. Sometimes it reads a URL out loud, letter by letter. Sometimes it skips a heading entirely.
The company had already tried to fix this. An earlier engineer built a pipeline that ran PDFs through Adobe's tools to rebuild document structure, then used Claude to patch in basic fixes. It worked, kind of. But our founder and CTO wanted something more specific: Claude and Gemini both checking every document, so the team could catch what either model missed on its own, and see which one actually read a document right. That part never got built.
What I inherited
The pipeline could tag a PDF's structure and write back basic content fixes. It called Claude once, for titles, alt text, table summaries. No accessibility audit step existed. No Gemini integration existed either.
What I built
Two modules, one calling Claude, one calling Gemini, running the same WCAG 2.2 audit against the same document. Started at 8 categories, alt text, headings, tables, reading order, links. Grew it to 13 over time, adding document metadata checks, struct-tree role validation, PDF/UA-1 rules pulled from the Matterhorn Protocol, form and language checks.
On top of that, a triangulation layer, something that lines up Claude's and Gemini's findings on the same document and shows where they agree and where they don't. That was the actual ask from leadership. Not just running two models side by side, but being able to say which one you'd trust on a given page.
Then there were the bugs you only find by actually testing with a screen reader. JAWS reading a link's label and then its raw URL back to back. VoiceOver reading list bullets and repeating header images like they were content. Heading levels fighting each other on the same page. Fonts failing PDF/UA-1 checks because they weren't embedded right. I fixed all of it.
I also built the frontend from scratch, a Next.js upload interface with live job status and a download button, as its own project first, then folded it into the pipeline. And a FastAPI layer so the whole thing could run behind a web upload instead of only from the command line.
A test that didn't turn into a decision
Where it stands now
I'm evaluating a third model for OCR-heavy documents, and finishing a fallback to a different PDF vendor for when Adobe's API quota runs out. Not done yet, but close.