Architecture

Daily batch pipeline: an ADK 2 workflow graph drives extraction against Gemini, a Vertex batch job backfills historical PDFs, and everything lands in one Firestore that this dashboard reads.

+ manual / admin Cloud Scheduler 6:00 AM daily Cloud Run carb-api /run

The decision logic (every run)

CarbLegal decision flowchart WAKE-UP REST PER DOCUMENT YES NO YES NO YES NO NO YES YES NO YES FAIL YES PASS YES NO YES NO next item (process) — per claimed item, steps below 6:00 AM — Scheduler rings failed work from last time? why did it fail? quota blip → requeue (heal) bad file → re-download from CARB, requeue (refetch) 3rd strike → leave for a human new EOs in the state registry? download PDFs, add to queue (scout) work in queue? time & budget left? write run summary — sleep until tomorrow (summarize) claim oldest item (claim) can Gemini read the PDF natively? re-render pages as images, retry readable now? mark failed — tomorrow's run decides structured extraction (Gemini 3.7 Flash) header EO number matches the file's identity? trust the file, log the misread deterministic gate: rules + baseline (audit) (dates real · formats sane · physics possible · vs. legacy) critic re-reads the document (Gemini) resolved? human review queue (via carb-dash) → approve / correct → re-match any ambiguous vehicle rows? Gemini resolves → fake IDs filtered document says "supersedes and cancels"? mark old EO superseded — link the chain write part-to-vehicle matches

Runs on: Cloud Run · Firestore (leased work queue = the checkpoint) · GCS · Vertex AI. Historical bulk backfill used Vertex batch pricing through the same gate.

The gate logic mirrors pipeline/workflow_graph.py and agents/* — node names tagged in (parens); verified by test.