Inspiration
City Council decisions shape daily life: which streets close to cars, who gets help in a heat wave, what gets built next door. But a single item is spread across a staff report, a committee recommendation, Council amendments and recorded votes, often hundreds of pages of legal and planning language. The chance to comment is short and easy to miss, so most people hear about a decision after it's made.
Only 20% of Canadians have ever taken part in a municipal consultation, and renters, newcomers, students and shift workers take part least. We built Plain Council for them, and for the City staff who need accurate, neutral summaries they can publish with a record of who approved what.
What it does
A resident types the first three characters of their postal code (only the area, never stored) or picks a ward, and gets a Decision Card for each item:
- Where it stands, in one line: "Decided by City Council on May 21, 2026, with changes to what was recommended."
- Can I have a say? "Open" only when the City's own item page links a comment form. A deadline is shown only if the City states it; we never guess one from a meeting date.
- In plain language: short sentences, each tagged with the step it comes from (staff recommendation, committee recommendation, or Council decision). Tap any sentence to see the exact words in the official record, with the document and date.
- What changed between steps: the official wording of each step compared word by word with the next, so amendments can't hide.
- How it got here, step by step: each outcome, and recorded votes labelled as votes on an amendment or on the whole item. Written submissions are counted, never named or summarized.
- Follow-up work Council asked for, from the City's Report Request Log.
Behind it, a staff review screen where a clerk approves, edits or rejects each sentence. Nothing is published until a person approves it.
Tested on three real items:
- Church Street pedestrian pilot: decided, amended at Council.
- Air Conditioner Benefit: decided, amended at committee and at Council.
- 1337 Queen St W heritage permit: upcoming; comments open through the City's form.
How we built it
- Facts from code, words from AI. A Python pipeline reads each item's official page and staff report PDF, plus Toronto Open Data's Voting Record and Report Request Log. Every date, step, outcome, vote, ward and participation status comes from those records. AI never decides them.
- Drafting. OpenAI's open-weight gpt-oss-120b on Groq drafts plain-language sentences, one step at a time. Each sentence must carry a quote from that step's source.
- Checking. Code rejects any sentence whose quote isn't in the source. It flags dates not found in the quote, deadline words, "decided" used for a recommendation, and loaded words, and measures reading level. A second AI call checks each sentence against its quote.
- Review and publishing. Supabase stores everything; database rules let the public read approved sentences only. A database trigger records who reviewed what and when, and locks each sentence's source quote. The Next.js site on Vercel caches cards so they're fast on phones.
- Postal area → ward is precomputed from Statistics Canada and City ward boundaries into a static file, so a resident's postal code never leaves the page.
- Built with AI coding help. Claude Code planned and wrote most of the code; Codex wrote the first version of the record readers from a spec. All of it is covered by 62 automated tests.
Challenges we ran into
- AI summaries that sound right but aren't. Of 63 drafted sentences, the automatic checks flagged 11. Human review then reworded 27 and rejected 6. The problems included an invented detail ("red brick"), a contradiction about building height, a dropped condition that made a study sound one-sided, and committee recommendations worded as if they were orders. That's why a person approves every sentence, and why every sentence shows its source.
- Plain language. The first drafts copied legal wording (grade 17). After new drafting rules and a simplification pass, the published cards read at about grade 7–10.
- Never guessing a deadline. Meeting dates are easy to find; comment deadlines often aren't stated. The card says only what the City's page says.
Accomplishments that we're proud of
- Sourced and reviewed. Every published sentence links to its exact source, and a person approved it.
- Amendments in the official words. A code-only "what changed" comparison shows them exactly.
- Privacy by design. No resident accounts, nothing about residents sent to AI, and no names of people who wrote in, even in our test files.
What we learned
- The hard part isn't generating summaries; it's knowing which sentences are wrong.
- Quote-matching and stage tags catch a lot, but a human reviewer is still essential.
- Clerk time, not AI cost, is the real cost.
What's next for Plain Council
- Pilot: three months, one committee. A clerk who didn't build the tool reviews each card, about 15 minutes per item.
- Measure corrections per card, time to publish, visits by ward, and clicks to the City's comment form.
- Schedule the jobs. Run the re-check and new-item scripts nightly. Anything new stays unreviewed until approved.
- Reach more people: French and other languages (reviewed), and a printable poster with a QR code for libraries, laundromats and tenant associations.
- Other municipalities: only the meeting-site reader is Toronto-specific, so a city on eSCRIBE or Granicus writes one reader and keeps everything else.
Hosting and AI cost about US$50 a month for a pilot. See the Municipal Readiness Report: docs/Readiness-Report.pdf.
Limitations, stated plainly: the published cards were reviewed by the developer after an AI pre-check, not by an independent clerk. Checks compare each sentence with its own quote, so they can't prove nothing was left out (amendments are shown word for word to cover that). Only recorded votes appear. Postal area → ward is approximate; the resident picks from a short list, and the City's address finder is linked.
Trying it out: demo email: [email protected] / password: demoplaincouncil123_
Built With
- claude-code
- codex
- github
- gpt-oss-120b
- groq
- next.js
- node.js
- pdfplumber
- postgresql
- python
- rapidfuzz
- react
- shapely
- statistics-canada
- supabase
- tailwind-css
- toronto-open-data
- typescript
- vercel