Case Study Field Service & Construction AI-Native Platform Live at cdohr.ai

California labor law,
compiled into software.

CDO replaces the ConnectTeam + Housecall Pro + spreadsheet stack with one system where the labor code is a design requirement — not a reporting afterthought.

Built and operated by IPS. Live in production with its first customer: a 65-person California roofing crew clocking in from the field every morning. Crews punch in with GPS and geofence evidence, paid to the minute with zero rounding. Every AI-initiated write drafts into a proposals queue and waits for a human.

~210
Postgres tables, RLS on all
153
Supabase edge functions
65
Person crew live in the field
300+
Daily clock events
01 / The Problem

Three tools.
None of them know the law.

A California field-service contractor runs on a stack: a workforce app for punches, a job-management product for estimates and invoices, and a spreadsheet to reconcile the two before payroll. Each tool is fine at its own job. None of them knows what the California Labor Code requires.

So compliance becomes a reporting exercise. A missed meal break is discovered weeks later, when someone runs a report — long after the premium was owed and long after the shift where it could have been prevented. CDO inverts that: the law runs at punch time, not at report time.

The stack it replaces
ConnectTeam (punches) + Housecall Pro (jobs) + spreadsheet (payroll reconciliation)
What one system makes possible
  • +Punch, geofence check, and meal-law clock are the same event
  • +§512 meal violation caught before hour five, not after the pay period
  • +§226.7 premium pay pre-filled into payroll the day it happens
  • +Estimate → invoice → Stripe payment on the same job record
  • One audit trail instead of three tools and a reconciliation step
02 / The Governing Architecture Law

AI is advisor.
Not actor.

This is the single rule the entire system is built around. The AI never writes to the database directly. Every AI-initiated change is drafted into a proposals queue, waits for explicit human approval, then applies transactionally — with an immutable audit row and an undo path. There is no code path where a model mutates payroll on its own.

Every AI-initiated write, every time
AI drafts proposal Lands in proposals queue Human reviews & approves Applies transactionally Immutable audit row + undo
📝

Proposals, not writes

Model output is a draft record, not a mutation. It carries its reasoning, its confidence score, and its citations into the queue so the approver sees why before they see what.

🔒

Transactional apply

Approval applies the whole proposal or none of it. Partial application is not a state the system can reach — which matters when a single approval touches a timecard, a premium, and a payroll line at once.

Immutable audit + undo

Every applied proposal writes an audit row that cannot be edited, and every applied proposal can be undone. In a wage-and-hour dispute, the question is always “who changed this and when” — the system answers it by construction.

The same rule governs the autonomous agent layer. Long-running “Workroom” sessions built on the Claude Agent SDK operate under hard spend caps — $2 per session and $50 per month — and still cannot commit a write without passing through the proposals queue. Autonomy is bounded in two directions at once: what it can spend, and what it can change.

03 / The Field Layer

Pay to the minute.
Evidence on every hour.

Crews clock in from a Flutter field app. The device timestamp is authoritative and there is zero rounding — a worker is paid for the minutes they worked, not for the nearest quarter hour. Clock-in requires a GPS-verified selfie, and every hour on shift leaves a GPS trail behind it.

What a punch actually captures

GPS-verified selfie clock-in. Identity and location are captured at the same instant as the punch, not asserted afterward.

Hourly GPS trail. Every hour on shift is location-stamped — a continuous evidence record rather than two endpoints.

Haversine geofence math. Distance from the job site is computed deterministically at punch time — not inferred, not estimated by a model.

Off-site punch patterns surfaced automatically. Not a single flagged punch — the pattern across days and crews, raised on its own.

Why it is built this way

California wage-and-hour exposure is not theoretical for a field-service contractor. A PAGA or DLSE claim is won or lost on the quality of the time record, and the time record is either contemporaneous evidence or it is one party's word.

CDO's field layer is built as a defense posture: contemporaneous, location-stamped, unrounded, and immutable once written. The compliance artifact is a byproduct of doing the work — not a document someone assembles later.

Device timestamp authoritative Zero rounding PAGA / DLSE posture
💵

A deliberate omission: worker time surfaces never show dollars

The field app shows hours, breaks, and job assignments — never pay amounts. This is a design decision, not an unfinished feature. Time entry is about recording what happened accurately; putting a running dollar figure in front of a worker mid-shift changes the thing being recorded. Money lives on the payroll surfaces, where it belongs.

04 / AI Timecard Triage

Deterministic rules first.
The model reviews, then cites.

Timecard exceptions run through deterministic logic before any model sees them: haversine geofence math and meal-law computation produce hard facts. Only then does an LLM reviewer weigh those facts and return a structured verdict — with a confidence score, the California Labor Code sections it relied on, and pre-drafted worker messages in English and Spanish.

// four structured verdicts
VerdictMeaning
DISMISSException explained by the deterministic facts — no action needed
APPROVE PREMIUM§226.7 premium owed — pre-filled into payroll on approval
NEEDS FOLLOW-UPEmployee must clarify — EN + ES message already drafted
ESCALATE HRPattern or severity beyond a supervisor's call

The split — and why it holds

1
Deterministic layer
Geofence distance, shift length, meal timing, overtime thresholds. Arithmetic and statute math — computed in code, reproducible, and identical on every run.
2
LLM reviewer
Cross-signal judgment on facts it did not compute: does this pattern of punches, notes, and geofence misses add up to an honest mistake or something a supervisor should look at? Returns a structured verdict, never a free-text opinion.
And then it waits
The verdict is a proposal. A human approves it before anything reaches a timecard or a payroll line.
// shape of a triage verdict returned to the proposals queue
{
  "verdict": "approve_premium",
  "confidence": 0.94,
  "deterministic_facts": {
    "shift_minutes": 583,
    "first_meal_started_at_minute": 318,  // past hour five
    "geofence_distance_m": 41
  },
  "citations": ["Cal. Lab. Code §512", "Cal. Lab. Code §226.7"],
  "worker_message": { "en": "...", "es": "..." },
  "requires_human_approval": true  // always
}

§512 — caught before hour five

The meal-break clock runs in real time against the live shift, not against a report at the end of the pay period. The violation is prevented on the day it would have happened, which is the only time prevention is possible.

💰

§226.7 — premium pre-filled same day

When a premium is owed, it is computed and pre-filled into payroll the day it happens — not reconstructed in a spreadsheet weeks later when nobody remembers the shift. §510 overtime is computed on the same pass.

🗣

Bilingual by default, not by toggle

Language is auto-detected per worker and every worker-facing message is drafted in English and Spanish. Compliance documents are stored permanently in both languages — so the record of what a worker was told exists in the language they actually read.

05 / The Compliance Engine

Software that reads
the law itself.

Labor rules are not static, and a system that hardcodes them is wrong the moment they move. CDO scrapes Tier-1 legal sources directly, content-hashes every tracked section, and detects when the text underneath a rule changes — roughly 105 tracked citations across six source groups, scanned monthly with a digest email.

Source groupTypeWhat it governs
Federal eCFRAPIFederal regulation text, pulled from the official eCFR service
CA Title 8ScrapeCalifornia occupational safety and health regulations
leginfo statutesScrapeCalifornia Labor Code sections — §512, §226.7, §510 and related
EDDScrapeEmployment Development Department guidance pages
IWC Wage Order 16ScrapeThe wage order governing on-site construction, drilling, logging and mining
Change detectionContent hashPer-section hashing across ~105 citations — monthly scan, digest email on drift
🗝

Tier-1 sources only

The engine reads the statutes and regulations at their official source. No secondary summaries, no compliance-newsletter paraphrase, no vendor's interpretation of what the rule means this year.

#

Content-hashed per section

Each tracked section carries a content hash. When the hash moves, the text moved — and the citation is flagged for review rather than silently continuing to back a rule that no longer says what it said.

🗺

Nine states, published rule guides

California is the deepest implementation because it is the hardest jurisdiction. Nine states now have published rule guides in the system, which is what makes the architecture portable beyond the first market.

06 / Money & Integrations

Estimate to invoice to paid.
Inside the same system.

The job that generated the hours is the job that generates the invoice. Estimates become invoices become Stripe payments natively — payment rails live and verified in production. Stripe mode is resolved server-side and accounts are scoped per legal entity, so a test key can never quietly take a real payment.

Native money flow

Estimate Invoice Stripe payment
  • Stripe mode resolved server-side — never trusted from the client
  • Per-legal-entity Stripe accounts, not one shared merchant
  • Payment rails live and verified in production

Meets the tools already in place

  • QuickBooks exports — the books stay where the bookkeeper already works
  • Payroll exports — premiums and overtime carried through already computed
  • Housecall Pro two-way sync — a contractor mid-migration does not have to cut over on day one

Published pricing is $29–$49 per user per month. The system is built and operated by IPS — every layer of it, with no vendor owning the customer's data.

07 / Tech Stack

One Supabase project.
Two front ends. No middleware.

A React 19 admin SPA and a Flutter field app talk to a single Supabase project — Postgres, Auth, Realtime, Storage, and Deno edge functions. There is no separate API tier to deploy, version, or keep in sync with the database.

LayerTechnologyNotes
Admin SPAReact 19, TanStack, Tailwind v4, Vite10 admin rooms, 5-level role ladder
Field appFlutter (iOS + Android)65 screens, GPS + geofence + selfie capture
BackendSingle Supabase projectPostgres, Auth, Realtime, Storage, Deno edge functions
Data layerPostgreSQL with RLS on every table~210 tables, 190 SQL migrations
AI routingClaude via OpenRouterTiered Haiku / Sonnet routing by task complexity
Autonomous sessionsClaude Agent SDK — “Workroom”$2/session and $50/month hard spend caps
PaymentsStripeMode resolved server-side, per-legal-entity accounts
LLM observabilityLangfuseEvery model call traced, costed, and inspectable
React 19TanStackTailwind v4 ViteFlutterSupabase PostgreSQLRow-Level SecurityDeno Edge Functions Supabase RealtimeClaude via OpenRouterClaude Agent SDK StripeLangfuseQuickBooks export Housecall Pro sync
08 / Production Footprint

What “live”
actually adds up to.

CDO is in production with its first customer. These are the numbers behind that sentence.

~210
Postgres tables
RLS on every one
153
Supabase
edge functions
190
SQL migrations
shipped
65
Flutter
app screens
10
Admin SPA
rooms
5
Level
role ladder
~1,500
Timecard transactions
per week
300+
Daily clock
events
~105
Tracked legal citations
across 6 source groups
9
States with published
rule guides
28
AI skills
in the system
$29–49
Per user / month
published pricing
09 / What Made It Hard

Five problems that only
show up in production.

Building a timeclock is easy. Building one that a California wage-and-hour claim cannot embarrass, that a 65-person crew uses every morning without training, and that an AI can improve without ever being trusted to write — that is a different problem.

01
The law as a design requirement, not a report
Every incumbent tool treats compliance as something you query after the fact. Making §512 and §226.7 enforce in real time meant the meal-break clock had to live inside the punch path itself — running against the live shift, computing premiums the day they are owed, and pre-filling them into payroll before anyone opens a report. Compliance stops being a discovery process and becomes a property of the system.
02
Giving an AI real work without giving it write access
“AI is advisor, not actor” is cheap to say and expensive to enforce. It means every AI-touching feature needs a proposal record, an approval surface, a transactional apply path, an immutable audit row, and an undo — before it can ship at all. The payoff is that the answer to “what can the AI break?” is architectural, not a matter of prompt discipline.
03
Deterministic-first insights — we don't call an LLM to do arithmetic
Anomaly detection runs on in-house robust statistics: median and MAD, robust-Z scoring, and quality gates that suppress a signal when the underlying sample cannot support it. A model is invoked only where cross-signal synthesis is genuinely needed. Arithmetic that a database can do exactly should never be handed to something that produces it approximately — and the same discipline keeps inference cost proportional to actual judgment.
04
Bilingual as an architecture problem, not a translation task
Language is auto-detected per worker and compliance documents are stored permanently in both English and Spanish — because the legally meaningful artifact is what the worker was actually told, in the language they read. One bug found here is instructive: the tier-routing classifier mishandled Spanish imperatives, sending them down the wrong model tier. It was caught and fixed against a 374-pair test corpus built specifically to exercise the routing boundary.
05
One project, two front ends, no middleware to drift
A React 19 admin SPA and a Flutter field app share a single Supabase project. That removes an entire API tier — but it moves the burden onto row-level security, which has to be right on all ~210 tables because it is the only thing standing between a field worker's session and someone else's payroll data. 190 migrations later, RLS is not a feature of the system. It is the perimeter.
What's next

Ready to build something
like this?

IPS builds custom data platforms, intelligence systems, and AI-powered products from scratch — then operates them in production. We have done it for government transparency, sports analytics, cannabis CRM, enterprise sales AI, and now field-service compliance. Every layer is custom, and the customer owns all of it.