Data sources — owned elsewhere, referenced never copied
Objects — the stable nouns
Services
Access layer — the API every portal calls
Samsara geofence sync · one-way → core
Status · read by every window
Work a queue, emit Moves. The back office.
Own a core object over time. Management altitude.
Thin, role-scoped, mostly mobile. One human each.
Foundation
A portal is a view + workflow over shared objects, never its own database. When a portal wants its own backend, it's about to become a disconnected tool.
Modeling
The five objects rarely change; portals are role-scoped lenses. A new need is almost always a new view — not a new object.
Flow
Any door can create a Request or Move. Make the input as heavy as what's unknown — field form vs. shop button. Add channels without touching the back office.
Visibility
No separate reporting store — the CM portal reads the same status the desks already update, and it's where CMs approve the requests routed to them. Visibility is free when there's one source.
Data ownership
VisionLink owns yellow-iron telematics, Samsara owns geofences, the core owns identity and work state. Reference it; never copy it.
Resilience
Doers and drivers get dead-simple surfaces. Keep a human escape hatch so non-technical staff can read and fix data without routing through you.
The slab: the five objects in one D1 database, the access layer every portal calls, and the one-way Samsara geofence sync. Everything else sits on this — the cheapest thing to get right first, and it's done (schema, foreign keys, CRUD, the read API).
The planning surface for who's on what, over time — the most-read shared object. Replaces the "Foreman Quick Look" spreadsheet. The read-only board (two views — by job, by person) is live behind Cloudflare Access for the whole company; editing, job onboarding, and the spreadsheet import are the remaining steps. This is Planner Phase 1; the same timeline engine carries Phase 2 (Equipment) later.
It's the most-built portal and the sink every Move drains into. Finishing it on the core — reading a clean active-job list from Planner instead of its own data — proves the whole model end to end.
They exist and deliver value today — move them onto the shared schema gradually, one at a time, keeping the human escape hatch so nobody loses a grid they can touch. This also puts equipment on the core, which unlocks Planner Phase 2 (Equipment): planning iron onto jobs on the same Gantt, with plan-vs-actual against telematics.
Completes the fulfillment ring and closes the request → fulfill → Move loop. Equipment Desk is one tool (work orders + equipment-request fulfillment), Dispatch's sibling.
A conversational capture channel — crews text (or later call) a Twilio number in plain language; an AI agent asks clarifying questions and writes a structured Request. Equipment requests route to the CM for approval (read from Planner's assignments) before fulfillment — never to a second gate. Retires HeavyJob forms without adding another app.
Split the driver "page" out of Dispatch into its own surface once Dispatch is solid — different human, different job, different device.
The CM's window — view + approve. It shows live status across their jobs ("50% fabbed, ships Tuesday," "D5 leaves Thursday," "two down, mechanics scheduled"), and it's where the CM approves the equipment requests routed to them — the approval gate in the intake → fulfillment flow. The CM owns the cost decision; David isn't a second gate above them. It comes last because it only matters once the desks beneath it are emitting status and routing requests up for approval.