Inspiration

Regulated supply chains all share one problem: the official record drifts from reality every day. I spent six years running licensed processing operations in the most extreme case of it, state-tracked medicinal cannabis, where METRC, the state seed-to-sale database, is legally the truth. Release decisions happen dozens of times a week by cross-referencing METRC's testing status against what the regulation actually requires, mostly from memory. Lab results land partially. Manifests arrive short. And METRC's TestPassed flag can read true while the panel the regulation requires is incomplete. A naive integration reads that flag and ships product to a dispensary.

What it does

Every hour, and the moment new evidence lands, a Cloud Run job scans the facility, computes in code which of the regulation's required analyte categories are actually on file for each package, has Gemini write the cited release/hold/escalate decision, places or lifts holds through a write-gated METRC client, and files a discrepancy report a compliance officer can act on. A public dashboard shows the run history and streams the agent's reasoning live. Judges can inject scenarios, or build worlds I never wrote with the included generator, and watch the agent notice on its own.

The core design decision: no model gets a vote on the safety rule. A package with an incomplete or failing test panel can never be released, regardless of what any model concludes or what METRC's status field claims. That invariant is enforced by the graph. The test suite proves it by scripting a model that approves everything and asserting the agent holds anyway.

How we built it

  • Google Cloud: Cloud Run (agent as a job; simulated METRC and dashboard as services), Cloud Scheduler, Firestore, Cloud Build, Artifact Registry, Vertex AI. No API keys anywhere: everything authenticates through the service account.
  • Three models, three jobs: Gemma 4 26B (managed on Vertex) screens every raw record for anomalies code can't see, like wrong-matrix potency results. Gemini 3.5 Flash writes every cited decision. Gemini 3.1 Flash-Lite independently countersigns every release, because real facilities require two authorizations before product moves.
  • LangGraph + the Google Gen AI SDK orchestrate the cycle. All data access goes over MCP: one server owns METRC's REST surface and the write gate, another owns the regulation corpus with required panels stored as structured data. Swapping the simulation for production METRC is one environment variable.
  • The regulation is the ground truth. 915 KAR 1:110 was re-derived from the published Kentucky administrative code, and the corpus itself is under test.

Challenges we ran into

Real METRC access requires a vendor agreement, so the system runs against a stateful simulation of METRC's documented v2 API. That constraint became the reproducibility story: anyone can clone the repo, run the whole system including the write path, and construct test cases I never wrote. Deployment had its own lessons (IAM propagation races, a scheduler that failed silently with zero retries, Google's frontend reserving /healthz), all documented in the repo's findings log.

Accomplishments that we're proud of

Over a hundred unattended hourly cycles with zero gaps and zero errors, running since August 24 with nobody watching. Releases that require two model signatures, and a write gate the suite proves cannot be bypassed: 84 tests across five packages. A scenario generator that turns the usual "you authored your own test" objection into the strongest reproducibility story we could build. And the agent's best single moment: it held our own "clean" seed data for violating a Kentucky potency statute we never taught it, and kept that hold for 44 straight hours until we fixed our data.

What we learned

The findings log has eight entries, and the pattern surprised me: the interesting failures were never the model being wrong. They were the model being right about something I got wrong. An early fixture carried only two of ten required analytes while METRC said TestPassed; the agent refused to release it. The live model escalated a gummy showing 78.4% THC as a probable wrong-matrix lab result, which was my bug. And the statute catch above: KRS 218B.095's 70% cap on finished vaporizable products lives in the statutes, not in the regulation corpus we encoded, yet the agent enforced it with the correct legal nuance, holding the finished cartridge while releasing bulk concentrate that is lawfully exempt.

What's next for Compliance Handler

The distance from demo to pilot is credentials, not code: complete METRC's vendor onboarding, point one environment variable at a state instance, and deploy in stages. Read-only advisor first, hold-only writes second, the full loop once the unattended run history has earned it. The same architecture covers pharmaceutical serialization (DSCSA), food traceability (FSMA), and medical devices (UDI). Medicinal cannabis is the instance where six years of operating experience checks the output.

Try it: live dashboard at https://dashboard-158724123013.us-central1.run.app (inject a scenario; the agent wakes on its own) · repo at https://github.com/AM253906/metrc-compliance-agent (five-minute judge path at the top of the README). Simulated against METRC's documented API; no production track-and-trace record was ever touched.

Built With

Share this project:

Updates

Submission history