Skip to content

fix: #150 follow-ups (drawer header contrast, Live heat sync, RX days race, IATA interval cap) - #169

Merged
dborup merged 8 commits into
masterfrom
codex/issue-150-batch-followups
Oct 2, 2026
Merged

dborup merged 8 commits into
masterfrom
codex/issue-150-batch-followups

Conversation

@dborup

@dborup dborup commented Oct 2, 2026

Copy link
Copy Markdown
Owner

Relates to #150

The four follow-ups from the review of the issue batch (#109–#126). Each item has a test commit followed by a fix commit. The fifth point in the issue comment (analytics warm-up vs. frontend retry) is not part of this PR.

1. Nav drawer header unreadable in the light theme

style.css gave .nav-drawer-header the background --surface-2 (white in light), while nav-drawer.css paints the title with --nav-text (white) and the close button with --nav-text-muted. The header now uses --nav-bg2, which the customizer already maps (navBg2 in THEME_CSS_MAP) and every preset pairs with navText. No new variable, so the customizer is unchanged.

  • Test: step (k) in test-nav-drawer-1064-e2e.js computes, from computed styles, the contrast of the title (≥ 4.5:1) and the close button (≥ 3:1) against the header background in light and dark.
  • master: light 1.00:1 (white on white), dark passes. Branch: both pass (title white on rgb(26, 26, 46)).
  • In dark mode the header changes from #232340 to #1a1a2e, the same colour as the top nav.

2. Live: Heat checkbox vs. heat layer

syncHeatToggleToMatrix(false) now also sets ht.checked = heatEnabled. Switching Matrix off after init also rebuilds the heat layer when Heat is on. Without that rebuild, the checkbox would now be checked while the layer stayed hidden.

New steps in test-issue-125-live-toggles-early-e2e.js:

3. RX Coverage: stale days response

drawCoverage() and loadBoard() each take a sequence number (coverageSeq, boardSeq) next to the mount generation from #136. A response, or a leaderboard failure, renders only if no newer request of its kind has started. As a side effect, the same now holds for coverage requests from quick pans and rx changes.

  • Test 13 in test-issue-124-rx-coverage-viewport.js: switch days 7 → 30, answer the days=30 requests, then release the delayed days=7 responses. The days=30 data must stay. A failed older leaderboard request must not replace a newer one either. Red on master (the days=7 leaderboard replaces the days=30 one).

4. Ingestor: iataWarnIntervalSec overflow

IATAWarnInterval() caps the configured value at 86400 s (24 h). Before, 1e10 overflowed time.Duration to a negative interval, so every drop was logged. The cap is documented in config.go and config.example.json; the field is not in the README.

  • TestIATAWarnIntervalClampsHugeValues: 1e10 s (direct and from JSON), MaxInt, 86401 s and a year all give 24 h; 86399 s stays as is. Red on master.
  • TestIATADropThrottleOldestTracksStaggeredEntries and TestIATADropThrottleOldestFollowsAnEarlierDrop cover the oldest lower bound with staggered and out-of-order timestamps. They pass on master, which is expected: they pin the existing optimisation and kill four mutants the old tests let survive (see below).

Tests

Suite master branch
test-nav-drawer-1064-e2e.js 12/13 (new light step red) 13/13
test-issue-125-live-toggles-early-e2e.js 16/17 with the new steps (Heat sync red); master's own file 14/14 17/17
same, live.js from before #135 2/17 (regression step: 8 of 9 toggles ×2) –
test-issue-124-rx-coverage-viewport.js 12/13 (test 13 red) 13/13
go test ./... ingestor (-run IATA) TestIATAWarnIntervalClampsHugeValues red green

Unchanged on master and branch: test-frontend-helpers.js has 2 favStar failures (known baseline). test-rx-coverage-escape.js fails on both with "could not locate row-builder end"; it is not run in CI. test-pr-1490-live-map-gpu-animations-e2e.js cannot load @playwright/test locally on either side; it is not in CI.

Mutants

All run in copies, never in the worktree:

Mutant Caught by
header background var(--surface) / back to var(--surface-2, var(--surface)) (k) light theme
drop ht.checked = heatEnabled #150 Matrix+Heat step
ht.checked = true Heat-OFF step, plus two existing Heat steps
drop the heat rebuild on Matrix OFF #150 Matrix+Heat step
live.js from before #135 #135 regression step
board .then / coverage .then / board .catch without the sequence check test 13
boardSeq not bumped test 13
clamp removed / cap 25 h TestIATAWarnIntervalClampsHugeValues
sweep leaves oldest = now …OldestTracksStaggeredEntries
insert always sets oldest = now both new oldest tests
insert condition without now.Before …OldestFollowsAnEarlierDrop
insert condition without len == 0 …OldestTracksStaggeredEntries (sweep count)

The four oldest mutants survived the tests on master.

Perf

None of the changes is in a hot path. Item 3 adds one integer compare per response, item 2 one assignment per Matrix toggle, and item 4 one compare per dropped message.

Notes

  • No new npm dependencies and no new test files, so test-all.sh and deploy.yml are unchanged. The Kpa-clawbot guard count is still 9.
  • scripts/check-xss-sinks.sh --diff and scripts/check-css-vars.js are clean.

🤖 Generated with Claude Code

dborup and others added 8 commits October 2, 2026 16:20
…150)

Step (k) in test-nav-drawer-1064-e2e.js opens the drawer in the light
and the dark theme and computes the contrast of the header title (and
the close button) against the header background from computed styles.

On master the light theme paints the header with --surface-2 (white)
under the white --nav-text title: 1.00:1. Dark passes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
nav-drawer.css paints the drawer title and close button with --nav-text
and --nav-text-muted, but style.css gave the header --surface-2, white in
the light theme. The header now uses --nav-bg2, which the customizer
already maps (navBg2) and every preset pairs with navText.

Default themes: title 17.1:1 on --nav-bg2 in light and dark.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…#135 (#150)

New steps in test-issue-125-live-toggles-early-e2e.js:

- Matrix and Heat saved ON, Matrix clicked OFF while init is held: Heat
  must be checked and enabled, its layer shown after init, and Matrix
  ON/OFF after init must bring Heat back as saved. Red on master: the
  checkbox stays unchecked while applyLiveControlEffects() shows the
  layer.
- The same with Heat saved OFF: Heat stays unchecked with no layer.
- #135 regression: leave Live while init awaits loadNodes(), come back,
  then one click on each of the nine toggles must write localStorage
  once. api() shares the in-flight request, so both inits continue when
  it is answered. Green on master (fixed by #135); against master with
  live.js from before #135 it fails on 8 of 9 toggles with 2 writes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…off (#150)

syncHeatToggleToMatrix(false) only re-enabled the checkbox, so after
Matrix was switched off during init it stayed unchecked while
applyLiveControlEffects() showed the heat layer. It now also sets
checked from heatEnabled.

Switching Matrix off after init also rebuilds the heat layer when Heat
is on, so the checked box is never shown without its layer.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Test 13 in test-issue-124-rx-coverage-viewport.js switches days 7 -> 30,
answers the days=30 coverage and leaderboard first, then releases the
slow days=7 responses: the days=30 data must stay. A failed older
leaderboard request must not replace a newer one either.

Red on master: the days=7 leaderboard replaces the days=30 one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…uest (#150)

drawCoverage() and loadBoard() each take a sequence number (coverageSeq,
boardSeq) next to the mount generation from #124, and a response (or a
leaderboard failure) renders only if no newer request of its kind has
started. An older days response can no longer overwrite newer data; the
same holds for coverage requests from quick pans or rx changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#150)

- TestIATAWarnIntervalClampsHugeValues: 1e10 s (also from JSON),
  MaxInt, 86401 s and a year must give a 24h interval. Red on master:
  1e10 s overflows time.Duration to a negative interval.
- TestIATADropThrottleOldestTracksStaggeredEntries and
  TestIATADropThrottleOldestFollowsAnEarlierDrop cover the `oldest`
  lower bound with staggered and out-of-order timestamps. They pass on
  master; they kill four mutants that survived the existing tests
  (oldest left at now after a sweep, set to now on every insert, and
  either half of the insert condition dropped).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
time.Duration(c.IATAWarnIntervalSec) * time.Second overflowed for huge
values (1e10 s became a negative interval), so every dropped message was
logged. IATAWarnInterval() now caps the configured value at 86400 s.
Documented in config.go and config.example.json.

Co-Authored-By: Claude Opus 5.5 <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