Inspiration

You can find out what's happening underground, without digging, by sending a small electrical current into the ground and measuring how easily it flows back. Oil, water, and different rock layers all conduct electricity differently, so that measurement (called resistivity) is a real, physical fingerprint of what's down there. Geophysicists have used it for decades to check for things like hydrocarbon contamination at a site.

Here's the same measurement, taken at three real survey sites: clean at Bori, clean at Etche, contamination warning at Choba. Same number, three real sites, three different verdicts, because each site's own ground composition sets what "normal" means there. It's a small, physical example of a general rule: global thresholds fail exactly where local baseline context matters, whether that context is a site's geology or an enterprise system's own operating history.

I have a B.Tech in Geophysics and did my industrial training at the Nigerian Geological Survey Agency, reading these underground surveys in the field before I ever wrote production code. The lesson from that field work was never "trust the formula." It was: the formula is only as good as the ground truth it's checked against. A single shared alert threshold across every survey site assumes every site's underground makeup is the same. It's occasionally right and confidently wrong the rest of the time. That's the actual problem VES Fleet answers, for the Fortified Enterprise Fleet track: institutional agents that hook into real infrastructure and interact with production data are only trustworthy if they respect the same ground truth a domain expert would insist on. Not one rule for everyone. Each site judged against its own real history.

What it does

A fleet of independent site-agents (Bori, Choba, Etche), each holding its own real underground survey history in isolation, coordinated by an orchestrator that watches for a risk signature appearing at more than one site within a time window: a possible shared cause, like a pipeline spill, instead of three isolated readings treated as noise.

Each site-agent computes its own risk-flagging threshold from its own real underground readings, not a number copied from another site's paper. Bori's computed ceiling (3,946.8 on the resistivity scale explained above) lands close to its own published paper's stated normal-background level (3,800), a real sanity check against the existing science, not a hand-picked number. Isolation is enforced in code: a site-agent that tries to read another site's history is rejected outright, not just discouraged by convention. New sites register into a shared catalog with zero changes to the orchestrator's own code. Every decision, per-site and fleet-level, writes an ordered, queryable audit entry, so a reviewer can trace one auto-filed case and one cross-site correlation notice back to the real source data and reasoning behind it.

Gemini, via Google ADK on the Gemini Enterprise Agent Platform, drafts the human-readable reviewer summary. It never re-derives the risk numbers themselves. Those come from deterministic code, the same design principle applied throughout: numbers this consequential shouldn't be re-derived by a model on each call.

How I built it

Google ADK orchestrates each SiteAgent's classify → calibrate → gate pipeline, running Gemini 3.5+ via the Gemini Enterprise Agent Platform (location="global"). Firestore holds per-site history (isolation-checked on every read), the agent registry, and the audit log. Pub/Sub decouples site-level escalation from fleet-level correlation: a SiteAgent publishes a RiskEvent on escalate, and a Cloud Run push subscriber runs the orchestrator's correlation check against a shared Firestore-backed recent-events store, so the check is correct regardless of which Cloud Run instance handles which request. All of it behind a service account scoped to exactly three IAM roles: Datastore User, Vertex AI User, Pub/Sub Publisher. Nothing more, confirmed via gcloud projects get-iam-policy before deploy.

Real, fully transcribed survey data for all three sites, from three independently published, peer-reviewed papers (13 stations at Bori, 1 at Choba, 8 at Etche). Choba's single real station means it doesn't yet have enough history to calibrate its own threshold, so it correctly falls back to a documented conservative default instead of miscalibrating off too little data. That's the spec's own "new site has no history" edge case, occurring naturally in the real data, not staged for the demo.

Challenges I ran into

The original spec named Bori, Ogbogoro, Onitsha as the three sites. I nearly built straight from that list. A check against my own prior project's data-provenance notes turned up that Ogbogoro's source paper has been unreachable for months (dead DNS, not a typo), and Onitsha only ever had a resistivity range from a search summary, used to fake a synthetic sample, never real station data. Two of three original sites had no real background to calibrate against, for a project whose entire pitch is calibration from real observed background. I swapped to Bori, Choba, Etche: all three fully real, transcribed, independently published.

The harder one: the orchestrator's cross-site correlation notice fired correctly, live, on the deployed service, the first time I tried it. It kept working. That worried me more than an error would have, because the reason it worked wasn't "the logic is correct." It was "Cloud Run happened to route both requests to the same running instance," since the correlation state lived in a plain Python list in process memory. That's correct exactly as long as traffic keeps landing on one instance, and not a guarantee. The real fix moved that state into a shared Firestore store, so every instance reads and writes the same record instead of trusting where traffic happened to land. Verified by submitting fresh station IDs live, twice, on separate runs, watching a genuinely new correlation notice appear both times against the shared store.

That bug is also the honest answer to whether the required stack here is load-bearing or just present to satisfy the rules: Firestore, Pub/Sub, and Cloud Run are load-bearing, provably, since this exact failure only existed because getting the state-sharing layer right actually mattered. Gemini is real and does real work every time a case escalates, but it isn't structurally required for the same guarantee. Remove it and every flag, gate, and correlation still fires identically; a human just writes the reviewer summary instead of getting one drafted.

A smaller one, caught by the same discipline: depths_m in the real station data is one element shorter than resistivities_ohm_m (the last layer is a half-space, omitted rather than null), which threw an IndexError the synthetic test data had never exercised. Fixed with a length guard once real data surfaced it.

Accomplishments that I'm proud of

Deployed and exercised end-to-end on Cloud Run against real Firestore, real Pub/Sub: the live service's own Cloud Run metrics show the actual traffic from these test submissions, not a mockup. Nineteen automated tests covering calibration, classification, isolation enforcement, registry behavior, and correlation logic. Four of those exist specifically because someone else's review caught a real gap: two concurrency negative controls (one proving an unlocked store actually races, one proving the Firestore-transaction fix doesn't) after a dev.to comment, and two more (a Gemini-drafting-failure resilience test, a dynamic-site-at-the-Fleet-layer test) after an adversarial code review caught that the registry supporting dynamic registration didn't mean the actual submission path did. Every CorrelationNotice this project can currently produce is honestly labeled constructed_scenario: true, since the three sites' real data comes from three independently published papers, not a coordinated regional survey, and there's no real synchronized multi-site dataset yet to detect a genuine correlation from. Said plainly rather than presented as live detection of a real incident.

What I learned

A demo that works once proves your happy path executed under whatever conditions happened to be true at that moment. It doesn't prove the mechanism holds under a condition you didn't happen to hit, like a second Cloud Run instance, or a site your original spec assumed had real data but didn't. "It worked" and "it's correct" are not the same claim, and a live demo will happily let you conflate them. The fix isn't more testing volume. It's stopping after each success, even the ones that feel earned, to ask what specifically made it succeed.

What's next

Real motivation for pushing past v1, not a claim about what v1 already does: UNEP's own Ogoniland assessment documented oil contamination reaching Niger Delta groundwater directly, the exact failure mode a per-site-calibrated, cross-site monitoring fleet is built to catch earlier than a shared-threshold system would. Nothing in this project currently detects that live. It's why closing the gaps below is worth doing, not a description of something already deployed.

An Agent Gateway-style unified routing/policy layer and a Model Armor-equivalent guardrail (prompt-injection, tool-poisoning, PII-leak checks) are both on the GEAP recommended list and both currently missing; the audit log and registry cover discovery and observability, but nothing here yet enforces access policy centrally. A live raw-curve submission path (running the ported pyGIMLi inversion pipeline from the original single-agent project against fresh field AB/2 curves, not just already-known layer resistivities) is the natural v2. And moving the 48-hour correlation window from a stated provisional assumption to something calibrated against a real multi-site incident, once one exists to calibrate against.

Built With

Share this project:

Updates