PrinciplesTraceSarkar · 8 min read
No model in the path: why we hand-write the post and the PDF
TraceSarkar uses AI to read a photograph. It refuses to use AI to write a sentence about anyone. Here’s where we drew that line, and why.
For a civic app with no marketing budget, the share is the growth channel. Every WhatsApp forward of a pothole record is an ad, a piece of evidence and a nudge to a ward office at once. So the text of that share matters more than almost anything else we write. We decided no language model would write it.
The temptation#
We already use a vision-language model to classify photographs — pothole, open manhole, burst pipe — with a confidence floor below which it has to ask. Having the same kind of model write a friendly, punchy post about the result would take an afternoon. It would read better than anything we’d template.
“It would write better sentences than a template. It would also, eventually, write a fact.”
That’s the whole argument. A model asked to describe a pothole on a contracted road will, one day out of thousands, add a contractor that isn’t in the record, or an amount, or a word like “negligent.” Filters catch vocabulary. They don’t catch claims.
What we do instead#
The share kit assembles the post from a fixed set of facts — the public view of an issue — using reviewed templates in English, Marathi and Hindi:
- 1
Start from the public view
Only fields in the public issue view can appear. What isn’t in it — the reporter, exact coordinates — is excluded by construction, not by remembering to leave it out. - 2
Fill reviewed templates
Strings live as reviewable JSON with a review state. Marathi and Hindi ship marked as drafts until a native speaker has read them. - 3
Check the finished string
A word-boundary check runs over the final post for three groups of terms: accusations of wrongdoing, insults, and political party names. If it fails, Compose refuses to return the text at all.
The tone is set by one rule from our brand notes: say “48 hours have passed,” never “BMC has failed you again.” State facts and joins; never conclusions.
“The failure mode is a stilted sentence, not a false one.”
The share kit caught our own bug#
Building the deterministic post forced us to say exactly which deadline applies to what. And it turned out we’d been sloppy: the High Court’s 48-hour rule binds potholes — not every road defect. A model would have happily kept writing “48 hours” under every cracked footpath. A template that pulls the deadline from a cited table couldn’t, and the mismatch surfaced the moment we wrote the test.
That’s also why no legal constant is a literal in our code. The 48-hour corporation deadline, the 24-hour contractor deadline during the warranty period, the ₹5,000-a-day penalty — each lives in a versioned table with an effective date and a citation.
The evidence PDF, written by hand#
The same thinking produced the strangest piece of code in the repo: a hand-written PDF writer of around 400 lines. Off-the-shelf libraries were fine at drawing pages. The problem was what they add quietly — a creation timestamp and a random document ID in every file.
An evidence record should be reproducible: the same facts should produce the same document, and anyone should be able to check that. So our PDF is deterministic, and it’s stamped with a hash over the facts, not the rendered bytes. Citations that would run off the page count as no citation at all. URLs never hyphenate. Only the person who reported a sighting can download it.
The line, stated plainly#
Use models where their mistakes are cheap and caught — sorting a photo into a category, with a human-facing “are you sure?” when they’re unsure. Never use them where a mistake becomes a published statement about a named person. That line costs us some nicer sentences. It’s the cheapest insurance we’ll ever buy.
Keep reading
- Bands, not percentages: a spatial join you can’t afford to get wrongHow TraceSarkar matches a photo of a pothole to the ward that owns the road and the contract that paid for it — and why every gate is built to ask rather than guess.Building · TraceSarkar
- Safe by construction: privacy engineering for a civic app on a zero budgetFaces destroyed before any model sees them, locations snapped instead of jittered, and database constraints that make the unsafe path impossible rather than unlikely.Building · TraceSarkar
- Making a housing society’s books balance — in paiseWhy ViharSync keeps a real double-entry ledger, stores every rupee as an integer, and lets Postgres refuse any journal entry that doesn’t add up.Building · ViharSync