NDX Fuel/Lube — HANDOFF

Written 2026-08-27 PM PDT. Read _CONVENTIONS.md in the val first, then this.

Who / what / where

  • Kaden Fieseler, owner, NDX heavy-equipment field service, Las Vegas.
  • Val: kfieseler/ndx-parts-dev (DEV/staging). Public URL: https://kfieseler--ffd44ea08d0211f195b01607ee4eb77e.web.val.run
  • Goal in flight: move the fuel/lube module into the LIVE app. Kaden is working the kinks out now, in multiple chats at once. Assume the val moved under you.

HARD RULES — do not soften, do not "just test"

  1. BusyBusy is READ-ONLY. No writes, ever — unless Kaden gives permission, and you have to ask twice. There is no write path; do not build one. Manual time is always: the assistant drafts the rows, Kaden keys them in.
  2. REAL DATA ONLY. No sample/demo/placeholder rows anywhere in the product.
  3. Never write current_hours to a Tenna ASSET record — it may register as a manual meter adjustment and silently distort fleet utilization. (fuel_entry.current_hours is a different field and is safe.)
  4. TENNA_WRITE_KEY requires per-batch approval + a one-row canary. Write API always returns HTTP 200 — parse errors[], never the status. approvers / vendors / schedules / warranties / registrations are destructive replace-sets — omit the key to leave them alone.
  5. Never trigger a write to test that a write works. Abort before commit. If one slips through, fix it the same turn and say so out loud.
  6. Never automatically alter a route. Draft/auto-push never overrides a manager's push.
  7. Site pins / fl_site_link mappings are manager-owned — added only on Kaden's explicit go.
  8. Suspect paper meters are NOT corrected without Kaden's confirmation.
  9. Hours-audit findings stay private to that chat. No accusations about people.
  10. Never send/contact/write to Monday without Kaden's go.
  11. No personal names in fueler-facing UI. Approved wording is "the manager".
  12. Do not fabricate GPS accuracy. Always distinguish planned route data from GPS-verified actuals.
  13. OSM tiles: keep the © OpenStreetMap credit.

Platform traps that will burn you

  • BUILD constant, main.tsx ~line 13. /app.js?v=BUILD is served max-age=31536000, immutable and the service worker caches under ndx-<BUILD>. Editing any client file changes NOTHING on a phone until BUILD moves. It has moved 335 -> 336 -> 338 -> 341 -> 347 -> 349 under this work because of the parallel chats. Always read the CURRENT value from the val before bumping. Live value right now: DEV-349.
  • FUEL_JS in fuel.ts is a TS template literal. NO BACKSLASHES ANYWHERE — not in strings, NOT IN COMMENTS. An escaped-n inside a comment becomes a real newline, splits the comment, and the rest of the comment executes as code. Use String.fromCharCode(10). This has caused two separate syntax breaks.
  • Nothing on Val Town may outlive the request that started it. An unawaited promise dies with the isolate; libsql then throws resp.body?.cancel is not a function and 500s the NEXT unrelated request. Run check_floating.ts before shipping. 26 floating promises have been fixed.
  • Run check_floating.ts and _shipcheck2.ts before every ship.
  • Tenna read API is NIGHTLY. asset-readings-daily returned EMPTY for 8/26 and silently disabled every meter check that depended on it. Same-day meters exist only via fuel_entry_asset_hours, and only after a reporting refresh.
  • Pacific offset: pacToday() = Date.now() - 7h.
  • OPEN_LOGIN = true in DEV — any PIN works. Must be flipped before prod.
  • The classifier blocks bulk direct SQL writes — route them through in-val scripts.

Shipped and working (this session)

  • Run-hours anchor: dash-read fallback plus a physics ceiling (a dash anchor is capped at 24h/elapsed-day; a real Tenna reading is not). Caught 4105/2 before it rendered "ran 3,833h".
  • Meter trust stack: fuel_meterflag.ts (keyedMeterFlags, impossible keyed meters, surfaced in the Review tab AND histlog) + stuckSweep() (meters that do not move between fills) + movedNote() ("moved today" / "has not moved in N days"). First sweep auto-caught 607/2.
  • All floating promises awaited; check_floating.ts added as a permanent guard.
  • Nav restructure: Today is today-only, Coverage moved under Route, crew sees one tab, boss gets a View-as-crew toggle.
  • Route page: green SENT panel, planner seeds from SENT (not DRAFT, which was resurrecting Durango), read receipt ("Opened by X") + double-send confirm.
  • Builder: move-to-truck buttons on the collapsed stop header + a removed-stops tray with put-back.
  • Close-out Option B shipped (summary on the stop card). Option A shelved until after the prod cutover.
  • _CONVENTIONS.md written into the val as the cross-chat contract.

OPEN — awaiting Kaden, do not act without an answer

  1. 8/28 route was pushed twice (actually triplicated). Duplicate job ids 343, 344, 346, 347; keeping 345 and 348 was proposed. Permission to delete has NOT been given. Do not touch route data. Also unanswered: is the intended split 4101 = Chinatown + 7-Eleven, F250 = Skye Canyon + Silverado + MC4?
  2. 8/27 route as pushed had Skye Canyon twice and a "Yard" stop — never answered whether to remove them.
  3. Bad meters, not to be corrected without confirmation: 4007/2 (111,430h), 315/2, 4105/2, 4008/2, 801/2 (15,804 vs live 15,412.9, likely 15,404), 311/2, 906/2, 602/2, 406/2, 307/2, 509/2.

OPEN — agreed, not yet done

  • Double-push race (CAUSE OF THE 8/28 TRIPLICATES). Send has no in-flight guard and the server does a non-atomic delete-then-insert. Fix = client busy guard on Send + server-side dedupe by (date, truck, jobname). This was promised regardless of the route-data answer. STILL NOT DONE.
  • Unit-suffix match bug — INVESTIGATED, NOT PUSHED. Tenna reports fills as 206/2; a manually added route stop can carry the bare 206. The two never match, so the fill lands in "Not on the route (Tenna)" instead of closing the stop. Confirmed on 8/27: the route DID have a Yard stop with unit 206, and the 20-gal fill came in as 206/2. Data check across all of fl_mach: only two forms exist — /2 (780 rows, 76 units) and bare (57 rows, 35 units) — and 206 is the ONLY base with both forms, so normalising on the part before / is collision-free against current data. Kaden said "okay lets push it" but the turn ended before any edit was made. No code was changed. Confirm scope, then implement.
  • Restore TRANSPORT_JS to the bundle (it was pulled for nothing).
  • Exception-list redesign: auto-close on next-day service, group by kind, bulk decide, sort meters above skips.
  • Rotate MONDAY_TOKEN — its description field holds the live JWT in cleartext. Kaden's to do, not the assistant's.
  • ~100 _-prefixed probe files to clean up, on his go.
  • Flip OPEN_LOGIN to false before prod.

Timecard drafting (BusyBusy) — the method

Source: busybusy-manual-time.md, revised 2026-08-24. Input is the geofence enter/exit export; Kaden supplies the clock-in and clock-out.

  • On-site geofence window = service time. Gaps between windows = travel, booked to 9999 - Shop Time.
  • Split each on-site window across the units fueled there weighted by gallons, minute resolution, 5-minute floor per unit, summing exactly to the window (largest-remainder).
  • Yard departure anchors the pre-trip block.
  • A vendor/fuel stop (e.g. River City Petroleum) is a security perimeter, not a job site — that time is Shop Time, not billed to a unit.
  • Ariel + Makana share truck 4101 = one route, one shared card, IDENTICAL rows under both names.
  • AM/PM trap: fuelers start at 3:00 AM. A 12-hour flip is a mis-tap, not a timezone problem. Exported timestamps are wall-clock — never tz-convert.

8/27 draft — DELIVERED, final

build_time.py -> Fuel-Crew-TIME-DRAFT-2026-08-27.csv. Ariel Vivas + Makana Kahalewai, 37 identical rows each, 3:00 AM - 2:00 PM, 11:00 total, 28 units billed. Verified independently from the written CSV: no gaps, no overlaps, per-row totals correct, no date drift, AM/PM correct. Timeline: 3:00 pre-trip/load - 3:40 fuel+grease 206/2 at the yard (20 gal) - 4:00 yard exit - 4:13-6:22 Chinatown (10 units, 286 gal) - 6:32-7:01 Silverado DMV (512/2, 6 gal) - 7:08-7:54 7-Eleven (3 units, 35 gal) - 8:15-8:48 MC4 (5 units, 30 gal) - 9:35-10:12 River City Petroleum (truck refuel, Shop Time) - 11:07-13:14 Creech (8 units, 130 gal) - 13:14-14:00 return/post-trip. Clock-in 3:00 AM and clock-out 2:00 PM are Kaden's, confirmed. The 20 minutes on 206/2 is an assistant estimate (gallons rate alone gives ~11 min; the rest is grease) — changing it only shifts the pre-trip block, not the daily total.

Working style that has worked

  • Test before claiming; the 4105/2 "3,833h" bug was caught by actually rendering it, not by reasoning about it.
  • Own incomplete work out loud — the first floating-promise sweep missed 4 calls and saying so mattered more than looking thorough.
  • Ask before touching route or meter data. Every time.