DefensibleScreen
A two-round resume screener where every rejection traces to something you can defend in court.
Inspiration
Hiring has a trust crisis on both sides:
- Recruiters drown in ~500 AI-polished applications per role and can't tell real from manufactured.
- Candidates can't tell whether a rejection was fair.
- Vendors who could explain their screening decisions won't. Mobley v. Workday showed that an opaque AI screener makes the vendor itself liable, so the industry's answer has been silence.
We thought that was backwards. Giving a reason isn't the legal risk. Giving an unstructured, proxy-laden, unlogged reason is. A screener that can only reject against pre-declared, job-related criteria, with quoted evidence and an audit trail, is the most defensible position an employer can be in. So we built it.
What it does
DefensibleScreen is a two-round resume screener where every rejection traces to something you can defend in court.
Pre-screening safeguards
- Criteria are locked before screening opens. The JD becomes a list of hard/soft criteria, each with a business-necessity note. A linter flags proxy language ("recent grad," "native English," zip codes) and drops it.
- Protected attributes are redacted before the model sees a resume: name, contact, address, DOB, education dates.
- Hidden prompt injection is caught and stripped: white text, 1pt fonts, off-page text, "ignore previous instructions."
Round 1: Criteria scoring
Each criterion is scored pass / fail / unclear with a verbatim quote.
| Outcome | Result |
|---|---|
| Hard fail | Rejection with a templated reason, e.g. "Did not meet: 5+ years Python — found: 2 years" |
| Unclear | Routed to human review |
| Pass | Advances |
Never a rejection on vibes.
Round 2: Provenance checks
Checks only the sources the candidate listed:
- GitHub account age: deterministic code, not the LLM
- LinkedIn employment dates
Severe contradictions reject; minor ones go to HR.
Human review and audit
- HR can advance freely but cannot reject without naming a criterion.
- Identity confidence comes from search-engine snippets (never scraping LinkedIn). It is informational and never a rejection input.
- Every model call is mirrored to PRISM by Block Convey as an independent, third-party-timestamped audit ledger: one session per candidate, with the raw resume withheld.
- Six themes, a live funnel, and a candidate-letter preview beside the redacted profile the model actually saw.
How we built it
| Layer | Tech |
|---|---|
| Backend | FastAPI + SQLite pipeline in Python |
| Frontend | React + Vite + Tailwind v4 with a token-based theme engine |
| Extraction | PyMuPDF for extraction and hidden-text detection |
| Ranking | Local MiniLM embeddings (queue ranking only) |
| LLM | DeepSeek V3.2 via OpenRouter for structured extraction and criterion scoring (pydantic-validated JSON) |
| Identity | DuckDuckGo snippets |
| Provenance | GitHub public API |
| Audit | PRISM (prismtrace) |
Decision rules are pure functions with unit tests.
Challenges we ran into
- The LLM got date arithmetic backwards. It told us a GitHub account created in 2016 was "more than two years after" a job starting in 2017, and nearly rejected a good candidate. We moved every date comparison that can trigger a rejection into deterministic code. The model classifies; code decides.
- Over-redaction. The model dropped employment dates along with graduation years, and invented "discrepancies" from facts that agreed with the resume. Both were fixed with tighter prompts plus code guards.
- Borrowed identity. A fictional resume's LinkedIn handle belonged to a real stranger with 500+ connections and scored "high" on identity. That's exactly the borrowed-identity attack, so we made employer corroboration a hard requirement for "high."
- LinkedIn has no third-party profile API and sues scrapers, so we designed around it rather than through it.
Accomplishments that we're proud of
- All six adversarial test resumes land in the right bucket:
- two advance
- one rejected on criteria
- one rejected on a provenance contradiction
- one flagged for hidden prompt injection
- one routed to human review for insufficient evidence
- The injected resume was neither helped nor auto-punished. The hidden text was stripped, the visible resume scored on its merits, and the flag surfaced to the recruiter.
- A legal risk map grounded in live cases (Mobley v. Workday, Harper v. Sirius XM, ACLU v. HireVue, the Eightfold FCRA suit) that maps each liability region to a concrete design countermeasure.
What we learned
Explainability in hiring isn't an ML problem; it's an architecture problem. If the model can only score against pre-registered criteria and only code can reject, the explanation writes itself.
We also learned that "compliance" features are the ones that make the product better for candidates: they get a real reason, and a human in the loop when the machine isn't sure.
Built With
- claude
Log in or sign up for Devpost to join the conversation.