Inspiration

Every neighbourhood already knows when something breaks. The group chat knows inside fifteen minutes. What nobody has is the stamina to do what comes after: file against the right body and not the wrong one, keep a statutory clock running for weeks, notice the day it breaches, and climb to the next authority with the whole record intact. Households do this alone, and institutions count on them giving up. Complaints get marked resolved with no work done. The same fault gets closed and reopened for months.

Panchayat is the neighbour who never gives up. Every household reports alone. The street gets the leverage.

What it does

One household types one line. Panchayat turns it into a pursued case:

  1. Finds the authority that actually owes the duty, with the statute that says so. Jurisdiction is looked up in a curated table for the neighbourhood, never generated by a model. Every routing decision carries a citation.
  2. Drafts the complaint in the household's name and waits for a real signature. Agents draft, humans sign. Nothing goes to a public body without a named person approving the exact words.
  3. Files it and starts the clock. The signed letter reaches the institution over A2A and comes back with a ticket number. On the deployed stack that takes 100 seconds from signature to ticket.
  4. Keeps watching after everyone else has stopped. A Watchdog wakes on every deadline, rides out an office that is down, and escalates a tier the day a window breaches. When a desk refuses a letter, the household sees the desk's own words and what happens next.
  5. Turns neighbours into a collective, with consent. When a second household on the same line reports the same fault and agrees to be counted, two cases become one case with two households behind it. Ten seconds, measured. The merge is reversible, the provenance is kept, and nothing about the inside of a house ever leaves it: only the fault and the street.

Twelve households reporting one outage stop being twelve tickets an office can close one by one. They become one file with twelve names on it and a clock the office cannot ignore.

We chose one place and one service for the demo where the failure is on record rather than imagined: a ward in Bengaluru and its water supply. The system does not know it is about water. It knows about jurisdictions, clocks, signatures and neighbours, and it reads all of that from data.

What is built, beyond what the demo shows

The demo follows one service in one ward because that is where the curated jurisdiction table has entries. Everything the table feeds is already general. Nothing in the spine knows it is about water.

The spine is service-agnostic. Cases and claims carry a service from a six-value set (water, power, sewage, garbage, roads, streetlight). Routing is a lookup on (service, street). Clustering windows, the merge, Anti-Abuse, the Watchdog, the escalation ladder and the case page all run on whatever service the entry says. The municipal ward desk already accepts water, garbage, roads, streetlight and sewage; the water board desk accepts water and sewage. Opening a second service is a YAML ladder with citations, and the site's intake gets one more option.

Jurisdiction with teeth. Every entry names the body that owes the duty and the body that does not (a complaint to the wrong office is the classic way to lose three weeks), the statutory window, the statute, the fields the office will refuse without, and a four tier ladder: first filing, reopen with corroboration, the state's service guarantee appeal with its compensation clause, and a Right to Information application that the system drafts and never files, because it needs a citizen's name, address and fee that we refuse to supply.

Consent is a real gate. Four scopes (file individually, join a collective, be listed publicly, spend money), granted per claim, revocable, with a disclosure ledger that records what left the house, to whom, and under which grant. The merge counts a household only when it said yes. The privacy warden minimises what crosses: income, health, arrears and schooling never leave. When a household withholds its reason for urgency, the system still knows there was one, and the Digest declines to lean on that household for a signature while another can carry it.

Deliberation inside the house. A household is a swarm, not a form. Members deliberate over shared context that never leaves the household, and the agent surfaces the deadline nobody typed: an elder's dialysis schedule turns "no water" into a case with a clock the family did not know to ask for. Intake and the Digest speak English, Kannada, Hindi and Tamil.

A Watchdog that survives the real world. Four wakes: the statutory check, the closure check, the unsigned draft expiry, and the resend. It disputes a closure marked resolved while neighbours are still reporting the fault, after asking the desk itself. It pauses the clock when a portal is down and retries. It records a refusal in the desk's own words, resends once, then holds the case for a person in a queue a person actually reads. It never files twice: a retry lands on the same idempotency key and the desk answers with the existing ticket. It never files unsigned: every tier needs its own signature, and the signature gate checks the filing belongs to the household signing it.

The street as evidence. Corroboration is the number of distinct households behind a case, never claims, so one roof reporting twice counts once. Recurrence is the number of prior failures on the same trunk main, and it is the argument the second tier is built on. Merges keep provenance both ways and can be split apart again; an absorbed case stops counting as a prior failure so one outage never inflates its own history.

Institutions that behave like institutions. Five desks with calibrated rates for refusing on a pretext, being unreachable, and closing without doing the work. Each is its own runtime with its own state and a ticket counter no other desk can touch. Their random behaviour is checkpointed so a retry gets the same office, not a re-roll.

Engineering underneath. Versioned case rows under three concurrent writers. One DynamoDB table with a single GSI. 660 tests run unchanged against the memory store and the DynamoDB code path. A linter rule that makes the time discipline non-negotiable. OpenTelemetry spans carrying the case id across the graph, the wakes and the desks. A trace UI that replays every transition for a report, with the citation on the routing line and an honest note on every cluster about which terms actually ran. An evaluation harness that drives a synthetic corpus through clustering, Anti-Abuse, drafting and the desks to measure routing accuracy and how the collective's odds change with household count.

How we built it

Most agent demos are one request in, one answer out. A pursued complaint is not a request. It is four different kinds of work, so Panchayat runs four execution paths:

  • Request: a Strands Agents graph on Amazon Bedrock AgentCore Runtime. Intake, household deliberation, a privacy warden that decides what may cross the membrane, grounded jurisdiction lookup, drafting. Eight agents, replayed live in the trace UI for every report.
  • Ambient: DynamoDB Streams into Lambda. Every claim is scored against its neighbours on topology and recency. It never gates anything and runs no model on a quiet street.
  • Temporal: EventBridge Scheduler into Lambda. The Watchdog keeps no state between wakes. One clock abstraction runs the same code as a compressed demo and as a real week.
  • Institutions: five simulated public and private desks, each its own AgentCore runtime reached over A2A, each with its own state and an IAM role explicitly denied access to ours. The trust boundary is enforced by the platform, not by politeness.

Underneath: one DynamoDB table, a single GSI, versioned case rows so three concurrent writers never overwrite one another, idempotent filings so a retrying Watchdog never files twice, and 660 tests that run unchanged against the in memory store and the real DynamoDB code path. Ten hard rules govern the codebase, written before the first line of it, and the linter enforces the first: nothing reads the system clock.

Adding a city or a service is YAML: the jurisdiction table, the escalation ladder, the desk profile. The agents read them.

Challenges we ran into

Clustering without embeddings. Our account's Bedrock model data plane is locked behind a support case, so the semantic term of the correlation score was unavailable. Scored as zero it capped the score below threshold and two houses on one main would never cluster, silently. We renormalise over the terms that did run and surface "semantic ran on 0/N pairs" in every trace, so the system never shows agreement it did not compute. Model calls sit behind a one line seam, so the provider is configuration: Gemini today, Bedrock the hour the case clears.

Institutions as real trust domains. Moving the desks onto AgentCore made every call a fresh process, and their calibrated behaviour replayed from the seed on every request. Fixing it meant treating their random state as state: checkpointed, versioned, written conditionally. The desks now behave like offices with memory, and nothing of ours can reach them.

Three writers, one row. The request path, the ambient merge and the Watchdog all write the same case. A wake that read a case, drafted for seconds and wrote it back could revert a merge that landed in between. Every case row now carries a version, every write is conditioned on the one it read, and a wake that loses the race reloads and finishes without sending the desk a second copy.

Accomplishments we're proud of

  • The whole institutional tail runs on AWS end to end, and the proof is a ticket number that exists in the desk's own table, which our role cannot read, and in ours.
  • Consent is a real gate, not a checkbox. Anti-Abuse refuses to count a household that did not agree to be counted, and we know because it refused us first: the merge did nothing until the form asked.
  • Merges are reversible and provenance never lies, on both backends, under concurrent writers.
  • A statutory clock that survives outages, refusals and retries, and says which one it is.

What we learned

We went in expecting a routing problem: parse the complaint, pick the office, draft the letter. A model does that in one call, and that is the version of this idea a weekend produces. It is also the version that makes things worse, because a confident letter to the wrong office is exactly how a valid complaint dies today. The real problem turned out to be social and temporal, and every piece of technical depth in the build follows from taking that seriously.

Time is the product. An office is not a function you call; it is a counterparty that answers in days, closes tickets without doing the work, and counts on you forgetting. So the system had to be built around wakes, not requests: a Watchdog that holds no state, a clock abstraction that runs a compressed demo and a real week through the same code path, and a lint rule that makes it impossible to reach for the system clock. The eleven-week pursuit is the feature, and a Graph invocation cannot hold it.

Consent is a data model, not a checkbox. "Count me with my neighbours" and "file in my name" are different permissions, granted per claim, revocable, and recorded in a disclosure ledger that says what left the house and to whom. The privacy warden sits on that boundary and lets only a Claim through: never income, health, arrears or schooling. We discovered the gate was real the day the merge did nothing, because the form had never asked and Anti-Abuse refused to count a household that had not said yes.

Trust boundaries have to be enforced by the platform. A Swarm shares a mutable context across every agent in it, which is right inside one household and catastrophic across households. That single fact decided the architecture: A2A for every institution, each desk its own runtime with its own table and an IAM role explicitly denied access to ours. The proof of the boundary is a ticket number that exists on both sides of it, written by a real call across it.

Concurrency is a social problem in disguise. Three independent writers, the request path, the ambient merge and the Watchdog, all touch one case, and a whole-item write from any of them could quietly revert what the others had done. The fix was versioned rows, conditional writes, and a wake that reloads and redoes without ever handing the desk a second copy. Idempotency everywhere, because a retry that files twice is the spam that gets both copies closed.

Absence is not zero. When the semantic term of the clustering score is unavailable, scoring it as zero silently caps the score below threshold and nothing ever clusters. Treating a missing term as missing, renormalising over what ran, and saying so on the trace turned a silent failure into an honest number.

And the meta-lesson: nearly every bug we found was the in-memory store and DynamoDB disagreeing, and the fix every time was running the same tests against both. Correctness lived in the parity, not in either backend.

What's next

Roads and potholes as the second service on the same spine, then more neighbourhoods, each one a data change. Verified identity through AgentCore Identity so a street cannot be faked from one browser. And two further tails on the same mesh: mutual aid through the school desk, shared cost through the tanker and payments desks.

The demo is one ward, one service, simulated institutions and no login. The pipeline underneath is real, deployed, and measured.

Built With

Share this project:

Updates

Submission history