Skip to content

feat(vehicle): config flags + per-vehicle overrides with HTTP polling#198

Merged
BK1031 merged 11 commits into
mainfrom
bk1031/feat-vehicle-config
Jun 10, 2026
Merged

feat(vehicle): config flags + per-vehicle overrides with HTTP polling#198
BK1031 merged 11 commits into
mainfrom
bk1031/feat-vehicle-config

Conversation

@BK1031

@BK1031 BK1031 commented Jun 9, 2026

Copy link
Copy Markdown
Contributor

Adds a config / feature-flag system to the vehicle service, with dashboard UI. Flags are defined per vehicle type with a default, overridable per individual vehicle; the car pulls its effective config over HTTP and reports back what it applied. Postgres is the single source of truth — no broker.

Backend (vehicle service)

  • config_flag (per vehicle type + default), vehicle_config_override (per vehicle), vehicle_config_status (applied version + last poll)
  • Effective config = type defaults merged with vehicle overrides, resolved on read. Version is a content hash, used as an ETag so an unchanged poll returns 304
  • GET /vehicles/:id/config — the car's poll endpoint (records poll, ?applied_version= ack)
  • GET /vehicles/:id/config/status — desired vs applied, in_sync, config_updated_at, last_polled_at
  • Flag CRUD scoped to vehicle type; deleting a flag cascade-deletes its overrides
  • Vehicle-type enum (gr24/gr25/gr26), validated on flag + vehicle create, exposed via GET /vehicle-types
  • Unit tests (merge/hash/validation) + Postgres integration test for the full flow

Frontend (dashboard)

  • Vehicle cards now navigate to a new /vehicles/:id details page
  • Details page: vehicle info (edit/delete) + a config section to create flags and set/clear per-vehicle overrides, showing effective value + source (default vs override)
  • Config status shows last-updated, last-pulled-by-vehicle, applied-at, and desired/applied version drift
  • Create-vehicle type dropdown driven by GET /vehicle-types

Not included (intentional): auth on mutating endpoints, audit/history table.

Validation: go vet + Go unit/integration tests pass; dashboard tsc --noEmit and eslint clean.

BK1031 added 11 commits June 8, 2026 22:42
…ling

- config_flag (per vehicle type + default), vehicle_config_override
  (per vehicle), vehicle_config_status (applied version + last poll)
- effective config = type defaults merged with vehicle overrides, resolved
  on read; version is a content hash used as ETag for conditional polls
- GET /vehicles/:id/config is the car's poll endpoint (ETag/304, records
  poll, ?applied_version= ack); /config/status reports desired vs applied
- flag CRUD scoped to vehicle type; deleting a flag cascades its overrides
- vehicle type enum (gr24/gr25/gr26) with validation on flag + vehicle
  create, exposed via GET /vehicle-types
- unit tests for merge/hash/validation; Postgres integration test for the
  full flow
- vehicle cards navigate to a new /vehicles/:id details page
- details page shows vehicle info (edit/delete) and a config section:
  create flags for the vehicle type, set/clear per-vehicle overrides, and
  view effective values with their source (default vs override)
- config status surfaces last-updated, last-pulled-by-vehicle, applied-at,
  and desired/applied version drift
- create-vehicle type dropdown now driven by GET /vehicle-types

Backend: GET /vehicles/:id/config/status also returns config_updated_at
(max updated_at across the type's flags and the vehicle's overrides).
- bump all in-major deps to latest (radix-ui, axios 1.17, recharts 2.15.4,
  mapbox-gl, xyflow, postcss, prettier, typescript 5.9, etc.)
- npm audit fix (non-breaking): 26 vulns (2 critical/15 high) down to 10
  that require breaking major bumps (vite/rollup/esbuild chain)
- cast dnd DraggableStyle to CSSProperties in WidgetSelectionDialog: newer
  radix tightens React's CSSProperties augmentation with a --radix-* index
  signature that DraggableStyle lacks

Framework majors (React 19, Tailwind 4, Vite 8, ESLint 10 flat config,
react-router 7, recharts 3) intentionally deferred — each needs its own
migration + QA.
New config endpoints fell through to the dashboard catch-all. The
per-vehicle config routes (/vehicles/:id/config*) are already covered by
the vehicles-id wildcard; this adds the two top-level paths.
Conditional GET added complexity (gateway would need a passthrough route to
preserve If-None-Match/ETag) for a small, infrequently-changing payload. Just
return the snapshot every poll; the car converges the same way and can use the
normal enveloped gateway route.
The car proves identity with ?upload_key= on the config fetch; a valid key
records last_synced_at (the fetch is the ack). A vehicle is in sync when
last_synced_at >= config_updated_at — pure timestamp compare, no content hash,
no applied-version echo, no one-poll lag. Dashboard reads (no key) don't count
as a sync. Removes ConfigSnapshot.version, SnapshotVersion, and the
applied-version fields from status.
Config, flag, override, status, and vehicle-type GETs are now Cache-Control:
no-store so browsers/proxies can't cache or revalidate them into 304s. Stale
flag data must never be served — a cached kill switch is unacceptable.
The 304s were harmless browser revalidation; not worth the extra headers.
Adds a per-flag delete button with a confirm dialog that warns it removes the
flag for the whole vehicle type and cascades to every override. Wires the
existing DELETE /config/flags/:vehicleType/:key endpoint.
One "Config" card now carries the in-sync badge, last-updated/last-synced
line, and the flag list. Also fix fmtTime to render Go's zero time
(0001-01-01) as "—" instead of a bogus date for never-synced vehicles.
One divider under the header; spacing separates the status line from the
flag list.
@BK1031 BK1031 merged commit 8bb1a4d into main Jun 10, 2026
18 checks passed
@BK1031 BK1031 deleted the bk1031/feat-vehicle-config branch June 10, 2026 00:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant