Skip to content

Ground truth: four colours for sim/live × ADS-B/dark - #357

Merged
jehanazad merged 2 commits into
mainfrom
feat/gt-four-class-colors
Sep 13, 2026
Merged

jehanazad merged 2 commits into
mainfrom
feat/gt-four-class-colors

Conversation

@jehanazad

Copy link
Copy Markdown
Contributor

Summary

Now that the fleet mirrors live-feed aircraft alongside its own spawns (#356), ground truth needs to answer two questions per dot: simulated or live-feed, and ADS-B or dark. This gives each of the four classes its own colour, in both themes, everywhere truth is drawn.

ADS-B (bright) no transponder (dark)
simulated TRUTH (neutral extreme, unchanged) TRUTH_DARK (grey, unchanged)
live feed TRUTH_LIVE teal-900 / teal-200 TRUTH_LIVE_DARK teal-600
  • map/truthColor.ts: one resolver (truthClass / truthFill / truthBorder / truthLegend) used by the live map's ground-truth canvas layer, the aircraft list's truth-only rows, the map legend (now four truth rows) and the Physics tab's preview map, so an object is the same colour on every surface. Anomalous and drone stay ahead of provenance; an older payload with neither has_adsb nor source reads as simulated ADS-B.
  • Teal is the one hue family no lane or furniture uses. Measured like the rest of mapPalette.ts (WCAG contrast against each theme's basemap + CIEDE2000 against every existing mark): every value clears its theme's floor (light ≥ 3.5:1, dark ≥ 4.7:1) and sits ΔE ≥ 13 from every other mark (worst: teal-600 vs the solver-seed lane on light), ≥ 22 from the neutral truth pair, ≥ 24 from its own partner.
  • groundTruthFixes carries source through to the fixes; the detail panel's Simulation section names it; the Physics tab's live card takes the teal accents and its badge splits the live count into ADS-B / dark (the dashed-ring hint on the preview map is replaced by the colour).

Test plan

  • truthColor.test.ts (new): class resolution, four distinct fills per theme, selection rim, legend/dot agreement
  • full frontend vitest run green; tsc --noEmit and vite build clean
  • live on the test droplet: Debug Truth overlay shows teal live dots beside neutral simulated ones in both themes; Physics tab legend/preview agree

🤖 Generated with Claude Code

The truth dots split two ways now that the fleet mirrors live-feed aircraft
alongside its own spawns: provenance (simulated vs live) and transponder
(ADS-B vs dark). One resolver, map/truthColor, gives each of the four plain-
aircraft classes its own fill and rim, keeps anomalous and drone ahead of
provenance, and reads an older payload with neither field as simulated
ADS-B. The live map's canvas layer, the aircraft list, the legend and the
Physics tab's preview map all draw through it, so an object is one colour
everywhere.

Simulated truth stays the neutral ramp (near-black / near-white, then grey).
Live-feed truth is a teal ramp — the one hue family no lane or furniture
uses — measured like the rest of the palette: every value clears its theme's
contrast floor and sits CIEDE2000 >= 13 from every other mark, >= 22 from the
neutral pair and >= 24 from its own partner.

  light  TRUTH_LIVE teal-900 #134e4a   TRUTH_LIVE_DARK teal-600 #0d9488
  dark   TRUTH_LIVE teal-200 #99f6e4   TRUTH_LIVE_DARK teal-600 #0d9488

The map legend lists the four truth rows; the detail panel names the source;
the Physics tab's live card takes the teal accents and its badge splits the
live count into ADS-B / dark. The dashed-ring hint the preview map used is
replaced by the colour.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@claude

This comment has been minimized.

Measured 21.9 from slate-800, but on the map a 9 px teal-900 dot beside a
slate-800 one read as two blacks. teal-800 keeps every floor (7.1:1; ΔE 13.1
from the seed lane, 25.5 from TRUTH, 19.3 from its teal-600 partner) and is
visibly teal.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@claude

claude Bot commented Sep 10, 2026

Copy link
Copy Markdown

Claude finished @jehanazad's task in 2m 49s —— View job


Todo list

  • Gather context (PR diff, CLAUDE.md, ONBOARDING.md)
  • Read the diff for all 11 changed files (both commits — including the follow-up bc34f03 light-theme teal-800 fix)
  • Trace truthClass / truthFill / truthBorder / truthLegend through every consumer
  • Verify backend field alignment (source, has_adsb, legacy "dark" literal) against backend/routes/test.py and backend/routes/sim_ingest.py
  • Post review

Review

Solid, well-tested refactor — one resolver replacing four copies of near-identical class/colour logic, and truthColor.test.ts pins the behaviour the PR body describes. I re-traced the full data path (backend/routes/test.py's meta.get("source", "sim")groundTruthFixes.tstruthColor.ts → the four consumers) for this synchronize push, which adds bc34f03 on top of the version already reviewed in the prior pass.

New commit (bc34f03) checks out: TRUTH_LIVE on light moved from teal-900 to teal-800 (mapPalette.ts:165) with an updated comment explaining why (teal-900 read as near-black beside TRUTH's slate-800 at marker size) — reasonable, and truthColor.test.ts doesn't hardcode the hex so it still passes.

One correctness nit worth fixing (new, not covered by the prior review pass):

  • PhysicsSettings.tsx:896-899acColor() now checks the legacy object_type === "dark" literal before calling truthClass(a):
    function acColor(a) {
      if (a.object_type === "dark") return SIM_DARK;
      return truthFill(truthClass(a), palette);
    }
    Previously this check was ORed in after the is_anomalous/drone checks. Now, an object that is both anomalous (or a drone) and carries the legacy object_type === "dark" literal would render SIM_DARK instead of the anomalous/drone colour — the opposite of the PR's own stated rule that "anomalous and drone stay ahead of provenance." In practice this is low-risk: nothing in the current backend emits object_type: "dark" anymore (grep confirms it's dead outside of output.py's legacy-payload doc comment), so it can only bite on an old recorded payload. Straightforward fix: resolve cls = truthClass(a) first and only apply the SIM_DARK override when cls is sim_adsb/sim_dark.
    Fix this →

Still open from the prior review pass (not addressed by bc34f03, flagging again since this is a fresh review of the current head):

  • LiveAircraftMap.tsx's MatchedGroundTruthLayer (the dot + dashed-line "error to nearest solve" overlay) still draws every matched ground-truth point in plain TRUTH, not the four-way class. Given the PR title says "everywhere truth is drawn," worth a one-line confirmation that this diagnostic overlay is a deliberate scope cut rather than a miss — no change needed if so.
  • TRUTH_LIVE_DARK sits at exactly the stated contrast floor in both themes (3.5:1 light, 4.7:1 dark) with zero margin, unlike most of the rest of the palette's comments. Not a bug, just worth knowing there's no slack if the basemap tiles shift.
  • The PR body's colour table still says "teal-900" for light TRUTH_LIVE; the code (correctly, per bc34f03) uses teal-800. Just the PR description text being stale relative to the second commit — no code change needed, but worth updating the description before merge so reviewers reading the table aren't misled.

Not run: same sandbox limitation as the prior pass — frontend/node_modules isn't installed and npm ci/network access isn't allow-listed here, so I couldn't execute vitest run, tsc --noEmit, vite build, or pre-commit run --all-files myself. The PR's test plan reports these green locally.

No API contract changes are implicated (nothing under /v1/nodes or node_config.py touched), so the contracts/nodes-v1.openapi.yaml gate doesn't apply here.

@jehanazad

Copy link
Copy Markdown
Contributor Author

Live on the test droplet (detached checkout of bc34f03, server rebuilt 22:49 UTC; fleet untouched):

  • Live Radar, Debug Truth on, dark theme: the ~60 truth dots over Greenville split visibly into near-white (sim ADS-B), grey (sim dark), mint teal-200 (live ADS-B) and teal-600 (live dark); legend shows the four truth rows.
  • Light theme: same four, near-black / grey / teal-800 / teal-600. The first cut used teal-900 there and at 9 px it read as a second black beside slate-800, hence the follow-up commit.
  • Physics tab preview map + legend agree with the live map (same resolver).
  • Detail panel names the source for a truth-only selection.

Left deployed on test.

@jehanazad
jehanazad merged commit 41c4c34 into main Sep 13, 2026
16 checks passed
jehanazad added a commit that referenced this pull request Sep 13, 2026
Resolves 21 conflicting files against 53 commits on main, including the
two pieces already split out of this PR (#351 identity scrub, #355
receiver-geometry withholding), #358 site markers / empirical coverage,
#348 location privacy, #350/#353/#354 owner contact and retnode links,
and the #343/#346/#347/#357 palette work.

Rules applied: main's version is the base for the add/add files
(public_geometry.py, test_no_real_identities.py); the PR's ref-keying is
layered on top of main's rework everywhere else; substitution still runs
last on every publication path and fails closed for unregistered real
nodes.

Follow-on edits outside the conflicted set, needed to compile or to keep
main's tests honest under ref addressing: frontend nodeSites.ts (+test)
now reads is_synthetic; backend test_node_ref.py and
test_public_geometry.py address routes by ref / synthetic id;
analytics.py's owner-private listing merge keys on the owner identity.

Verified: backend 3495 passed / 2 skipped, ruff clean; frontend tsc,
lint, vite build, vitest 232 passed; dashboard tsc, eslint, vitest 53
passed, build.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
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