Inspiration
More than a dozen US building-performance standards and dozens of benchmarking ordinances are now in force, each with its own thresholds, deadlines, denominations and portal. A portfolio owner with buildings in four cities employs a person whose entire job is a spreadsheet of deadlines and a login to each site.
The clocks are real money. NYC Local Law 97 penalties took effect May 1, 2026 — $268 per metric ton CO₂e over the cap, plus $0.50/sqft/month for a late report. Boston BERDO fines late reporters $150–300 per day with no annual cap, and the Review Board moved the 2026 deadline to Aug 15, so buildings that missed it begin accruing on Sept 14. NYC LL84 charges $500 per quarter.
Every compliance product we could find watches that spreadsheet and sends an email. None of them file. So we built the thing the reminder is about.
What it does
Ordinance autonomously files US building-energy benchmarking reports and returns a receipt for every one.
Laws in. Paste the verbatim text of a city's ordinance. Gemini 3.5 Flash compiles it into a jurisdiction pack — coverage thresholds, deadlines, penalties with their exact denominations, every fact tied to a citation. The pack is lowered into a small rules program with generated unit tests and diffed against an already-registered city. A human approves the confidence screen, and the fleet registers a versioned Jurisdiction Agent with a signed A2A card that then discovers the buildings it covers. Nothing about any city is hardcoded — retire the agent and its obligations and ledger lines vanish on the next compute.
The ledger. The Deadline Sentinel derives every obligation and a per-ordinance fine ledger from the registered packs alone: $/day after a grace period, $/quarter with a cap, $/sqft/month, and exposure shown as exposure and never booked as a fine. Each line carries the citation it came from.
The firehose. Utility-bill PDFs are screened by Model Armor before any model reads them — the bill carrying an embedded prompt injection is quarantined. Gemma 4 is the triage tier: it classifies each screened document and decides whether the expensive multimodal call happens at all, so a non-bill never reaches Gemini. Every number comes from Gemini 3.5 reading the PDF.
The filing. The sentinel finds an overdue building and hands the job to
filing_agent. Only the filer's identity may call file_*. The policy tier
checks four named rules — identity, readings coverage, idempotency, non-live
channel — the agent reads the building's filing history from Memory, files
through the channel under a visible badge, writes the receipt back, and the
ledger line stops.
Hand-offs are events, not sentences. Every delegation — ingestion → sentinel, sentinel → filer, sentinel → responder (automatic; nobody clicks), human → agent — is a typed trace row: from, to, task, transport, outcome. The fleet router emits them from what it actually did. No agent has a tool that can write one, so a hand-off in the trace is a fact, never model narration.
Where it stops. NYC LL97 reports must be certified by a Registered Design Professional (NYC Admin Code § 28-320.3.7) — a licence an agent cannot hold. That gate is not a demo policy we invented; it is a statute, and the fleet enforces it at the point the tool is called. The responder drafts, the gateway holds at the legal tier, a human signs, then it sends.
How we built it
Five ADK 2.8 LlmAgents, each with its own SPIFFE-style identity whose scope is a
set of allowed tool-name prefixes. A GatewayPlugin(BasePlugin) resolves the
caller and sorts every tool call into four tiers — auto, policy, human,
legal — before the tool runs, logging policy_allow, policy_hold or
policy_deny.
One codebase, three profiles. The dev floor runs on a laptop with a Gemini API key and nothing else. gcp adds Cloud Run, Firestore, a Pub/Sub push spine, the Model Armor API, Cloud Trace and Secret Manager. GEAP managed puts the five agents on Agent Runtime with Agent Identity principals and Memory Bank, reaches tools over MCP through Agent Gateway (IAP read-only condition plus Model Armor on bodies), and registers the MCP servers and every Jurisdiction Agent in Agent Registry. The in-process plugin explains the decision; the platform enforces it a second time.
The fleet's tools exist exactly once and are served both in-process and as three
MCP servers whose readOnlyHint annotations are what the gateway condition keys
on. The console is vanilla JS with an SSE trace feed — no build step.
Challenges we ran into
Our own agent fabricated data, and we caught it. The ingestion agent's
instruction said to use the readings triage returned — but the Gemma triage
response has no readings field, so on a confident triage the model filled in a
period and a quantity that were never on the page. It was non-deterministic: an
earlier run happened to call Gemini and passed. We did not prompt around it. Now
Gemini is the only source of a number, and store_reading compares utility,
period start and end, quantity, unit and cost against the bill's own extraction
record and refuses anything else — nothing is stored, the bill goes to
needs_review with the offending field named, and a policy_deny row says so.
The blast radius of a model that guesses is one refused tool call and a bill in a
queue, not a wrong figure in a filing.
The free tier. Twenty requests per model per day exhausts inside a single end-to-end run, so the code paces itself: a per-model sliding-window limiter and a quota registry that marks a model exhausted and falls down the chain, recording which model actually answered each call.
No billing account. Two accounts closed on us. Firestore, Pub/Sub and Cloud Trace provision without billing and are live; Cloud Run and Vertex AI do not.
Accomplishments we're proud of
The compiler was measured against verified ground truth on laws the build team had
never read: 47/47 critical facts on the four development ordinances, the
non-energy control law correctly rejected, and 25/31 (81%) on three never-seen
laws with zero fabricated numbers. The six deviations are encoding choices,
itemised in docs/gates.md.
The held-out set is sealed by a teammate who never touched the compiler;
seal_heldout.py records only a SHA-256, byte count, source URL and time, and the
server logs the access before it opens the file — so "genuinely unseen" is
checkable, not asserted. 369 tests run on every push with no cloud credentials.
What we learned
Governance is only real if it is enforced where the tool is called, not where the prompt is written. Every claim on our console is a rendered server field — the armor provider, the channel, the profile, the backend for each capability — so a label can never say "managed" while a first-party fallback is doing the work.
What's next
Wire the ENERGY STAR Portfolio Manager test environment as the default channel, bring the managed profile up on a real Google Cloud organization to verify the Agent Identity path end to end, and replace the remaining labeled synthetic rows as each city's official export lands.
Log in or sign up for Devpost to join the conversation.