PROJECT · active
PermitCheck
A Toronto permit-readiness checker that verifies a package against the property's actual zoning, and says what it can't confirm instead of guessing.
Next.js · Supabase · vision extraction · address-resolved zoning · eval-gated accuracy
limits resolved per address · flagged, not guessed
A wrong zoning answer costs a homeowner months at the Committee of Adjustment and a developer a fortune.
What it is
PermitCheck reads a Toronto residential building-permit or Committee of Adjustment package and verifies its readiness: is it complete, and does it comply with the zoning that actually applies to that lot. For a given address it resolves the binding limits, lot coverage, height, density, the front, side and rear setbacks, and any site-specific exceptions, from the City's official zoning data, then compares the applicant's own proposed numbers against them and cites the governing by-law section on every line.
The problem
Zoning is dense, cross-referencing, and unforgiving, and the binding number is rarely the generic one. The same zone can carry a site-specific exception that changes a setback, or an overlay that sets a different height or coverage for that exact parcel. The people who need answers most, homeowners and small builders, are the least equipped to read all of that, and a tool that confidently quotes the wrong limit is worse than no tool at all. This is a domain where the answer has to be right for the property in front of you, or clearly say it cannot be sure.
How it stays right
The honest part is what it refuses to do. Every limit is resolved for the specific address before any comparison, so a flag reflects the real binding number, not a default that happens to be close. When a value can be read and a limit can be confirmed, the comparison is computed in plain code with the by-law section cited. When the drawing can't be read cleanly, or the binding limit can't be confirmed, the report says "needs manual review" rather than inventing a verdict. The result is a tool that almost never cries wolf: when it flags a problem, it is overwhelmingly a real one.
How the accuracy got higher
A handful of measured choices, each tested against an evaluation set of real decided applications, with the City's own determinations as the answer key, before it shipped:
- I benchmarked a range of vision models on the same packages and picked the one that reads dense, busy drawings most accurately, which also turned out to be the cheaper option. Newer and bigger were not better here, and only the measurement showed it.
- I run the read more than once and reconcile the passes, which cancels the model's run-to-run misses on small text.
- I resolve the binding limit from the property's actual zoning rather than a generic table, which is what turns "we think this might be off" into a confident, sourced flag, and what eliminated the false alarms.
Every one of those was a number on a scoreboard, not a hunch. The bottleneck moved from "we don't have the rule" to "can we read the drawing cleanly," which is a far better problem to have.
What I learned
The product had to change shape before it could work. The original promise, predict whether a package will be approved, is unkeepable: at the Committee of Adjustment, approval turns on the variance and the neighbours, not on the document. The keepable promise is readiness, and reframing from prediction to verification is what made the system honest and measurable. The load-bearing principle is simple and absolute: never a confident answer off an unconfirmed number. Hold that line and a whole category of trust failures becomes impossible.
Status
Active and being extended. The pattern here, regulated documents plus address-specific ground truth plus eval gates, is the one I think matters most for the next decade of document AI.