ClauseGuard
Third place at the Intra-IUB AI Hackathon. A contract-review assistant built in a four-hour final that finds the relevant clause, compares it to the company standard, and returns a risk level with evidence, or refuses to guess.

Third place, Intra-IUB AI Hackathon 2026. Team Doomsday: me, Mahmudul Hasan Joy and Rahin.
The challenge
The final round was the Legal Tech Challenge: four hours, on site, with a sealed participant pack. The pack held eight fictional vendor contracts, seven company-approved clause standards for a made-up firm called Northstar Solutions Ltd. (payment terms, termination, data protection, confidentiality, auto-renewal, IP, liability caps), twelve public test questions, and three "missing information" traps where the clause being asked about was not in the contract at all.
The brief: given a contract and a question, return a risk level (Low / Medium / High / Not Enough Information), the exact clause used as evidence, the matching standard, a short explanation of the difference, and a mandatory "human review required" flag. The prototype must never claim to give legal advice, and it must never guess. Judging weighted search quality and ranking at 30 points, performance at 25, deployment and architecture at 20, then cost, observability, innovation and presentation.
Those "missing information" cases were the whole game. A naive RAG pipeline hands the model whatever it retrieved and lets it improvise. Ours was designed so the model could not.
What we built
Select a contract, ask a question ("What is the liability cap?"), and get a risk level, a plain-language reason, a suggested next step for the reviewer, and the evidence: clause text, standard text, source. One endpoint, one orchestrator, three library modules. A POST /api/review runs eight steps, and steps 2 to 4 are a deterministic abstention gate that decides whether the LLM is called at all.
- Cache lookup on
sha256(contract_id + normalized question). - Embed the question and cosine-rank it against seven precomputed clause-type embeddings. Below 0.55, abstain.
- If the contract has zero clauses of that type, abstain. The model never runs.
- Rank every clause of that type in the contract. Top similarity below 0.58, abstain.
- Fetch the matching company standard. Missing, abstain.
- Gemini Flash receives only the clause text and the standard text and returns strict JSON. Malformed output fails safe to "Not enough information".
- Assemble the result with evidence and
requires_human_review: true. - Write to the cache and insert a
reviewsrow for the audit trail, with approve / reject / flag actions for the reviewer.

The abstention path. When the clause is not in the contract the result says so, the evidence panels stay empty, and the reviewer still gets Approve, Reject, Mark for review and a feedback box.
Why it placed
The model is only ever handed two texts to compare, so it cannot invent a clause that is not there. We calibrated the thresholds on the pack itself: on-topic questions scored 0.60 to 0.87, off-topic ones ("hello", "who is joy") at or below 0.51, and the cut-offs sit in the gap, so every public test question passed and every trap abstained. The seven clause-type embeddings were computed once at seed time rather than on first request, so there was no cold start on the demo machine. Every external call is wrapped; a failure returns an abstention rather than a crash or a fabrication. It shipped on Vercel with Supabase and Gemini Flash, entirely on free tiers, which covered the cost category without a slide.
Not legal advice. An assistant only; a human makes the final decision.