H2O Professional Services · field ops suite
Everything below was verified by running it, not by reading the code. Rate the directions and copy your feedback back to Claude — your choices are saved in this browser.
| Target | State | Evidence |
|---|---|---|
| ST Bridge | running | systemd user unit, survives reboot; production tenant 4155131542, read-only, raw passthrough off. Board and performance both answer in ~0.7s. Recovers from SIGKILL. |
| Android | running | All 7 modules green. Verified live on a phone emulator and a Pixel Tablet against the production tenant. |
| iOS / watchOS | held | All 7 schemes build against Xcode 26.6 on the Mac mini. Dispatch board verified running in the Simulator. Deliberately parked behind Android — see the parity ledger. |
| Physical devices | untested | No handset or tablet has ever run this. Android needs a USB device; iOS additionally needs an Apple Developer team for signing. |
The suite's original problem was screens quietly showing invented data. This is where that stands.
| Screen | Android | iOS | Backed by |
|---|---|---|---|
| Dispatch board | live | live | /api/dispatch/board — 19 appointments, 10 technicians, customer names and addresses |
| Technician performance | live | live | /api/performance — revenue, jobs worked, WIP, memberships, conversion |
| Pricebook · Live tab | live | live | 783 priced services across 5 pages |
| Pricebook · other 4 tabs | sample | sample | Nothing to back them: materials and equipment have zero priced records in this tenant |
| Dispatch list | live | partial | Android: real day per technician, multi-column on tablet with the shared detail rail. iOS still on assignment-derived counts |
| Dispatch map | live | partial | Android plots 21 of 29 from real coordinates, fits the service area with outliers one tap away, full-bleed on tablet. iOS still geocodes technician home addresses |
| Admin dashboard tiles | 6 of 8 live | sample | Android: board + performance. Parts Scanned and Lowest Margin have no ServiceTitan source and are marked per tile |
| Admin activity feed | live | sample | /api/activity — assignments, sold estimates and completed jobs merged newest-first over 7 days |
| Home-screen widget | live | sample | Android: next job on the board, with the count remaining. iOS widget not started |
What Android has that iOS does not, as of now. This is the cost of letting Android run ahead — it is recoverable in one catch-up pass, but it grows every time Android ships alone.
| Behaviour | Effort to port | Note |
|---|---|---|
| Hold-to-peek | small | SwiftUI has a direct equivalent — onLongPressGesture(pressing:) gives press-and-release for free |
| iPad three-pane layout | large | The real work. Same shape, but SwiftUI layout, not Compose |
| Queue strip | small | Part of the three-pane pass |
| Map on real coordinates | medium | Bridge side is done — the payload carries lat/long. iOS map still geocodes technician home addresses and fails most of them |
| List as the real schedule | small | Same board data, a different row layout |
| Map fixes | n/a | Android-specific: the clipping bug and the Compose line-height fix have no iOS counterpart |
Rate each one. Nothing here is started.
LaunchedEffect that catches every exception also catches the cancellation fired by changing the date or period. That made the board claim it could not reach the bridge while showing data from the response that arrived. All fetching effects rethrow cancellation first.technicianId on appointments, date filters on dispatch/assignments, and startsOnOrBefore on appointments. All return 200 with wrong data. Diff filtered against unfiltered before trusting a new one.