Skip to content

feat(hub): say what the services report, and how old that is - #29

Merged
mikim merged 1 commit into
mainfrom
claude/health-honesty
Sep 22, 2026
Merged

mikim merged 1 commit into
mainfrom
claude/health-honesty

Conversation

@mikim

@mikim mikim commented Sep 21, 2026

Copy link
Copy Markdown
Contributor

Stacked on #28 (← #27 ← #26) — merge in order. This PR's diff is only its own commit.

The codebase's own rule is that a body may only look as alive as the data behind it. Before this PR, the map broke that rule in five ways.

What the page could not say

Situation Before Now
A service reports down (e.g. signalmap's deliberate 503 with a body) Every count stays the same: "12 health-checked" still reads 12. One 6 px dot turns red; that is colour only, and the colour legend is hidden below 900 px. A tally reads 13 ok · 1 off-contract · 0 degraded · 1 down · 0 unmeasured, followed by down: Signal Map. The body's label gets a [DOWN] tag, the sidebar row a down chip, and the tab title (1 down) Mossland Space Hub ….
Every health request fails (Wi-Fi drops, laptop wakes offline) The last snapshot stays forever, green and breathing, with the latency from the last good sweep, and no time is shown anywhere. The HUD always reads health checked HH:MM:SS. After 150 s with no landed sweep the reading is stale: bodies dim and stop breathing, the tally reads as of HH:MM, and the title says stale. Colours are kept; a stale reading is never counted as a current ok.
The first registry read fails "Loading registry…" for 10 minutes, until the next scheduled read. It retries at 5 s, 15 s, 30 s, then every 60 s until it loads. The map and sidebar read Registry unreachable — retrying, and the page recovers by itself.
AO's data API stops answering Its card only loses the LIVE badge, so it looks the same as before the first poll. The badge reads CONNECTING before the first verdict, then LIVE or OFFLINE (red badge and border, "no response this poll").
AO signals answer but its stats read fails The previous stats stay on screen beside LIVE, however old. Those figures show — with no stats this poll · last read HH:MM:SS.

Also: "signals ingested this session" (and its motes) now counts from each origin's first successful read. It used to depend on whether the first poll finished before the first 500 ms frame.

Rules the new states follow

  • Only services count, one bucket each: off-contract is never merged into ok, and unmeasured never into down (deliberate behaviours 2 and 3). Archived services are counted by their reading and tagged where named (algora is archived and off-contract).
  • The age comes from the sweep clock only (healthCheckedAt), never a service's own timestamp. For static artifacts that timestamp is the build time.
  • Hidden tabs don't flash stale. After the previous PR's pause, a tab shown again gets its sweep at once, and while that sweep is in flight the reading counts as refreshing. A background tab's title keeps its wording current.
  • Where the text lives. The non-colour cue on the map is text on the label, not a new shape: shape is already the FORM axis. The COLOUR legend now says so (down · also on its label, archived · in place of its health).
  • The counting and wording are in a Phaser-free src/ui/ecosystem-status.ts, shared by HubMap, Sidebar and the scene, with unit tests.

Between 768 and 1100 px, where the map HUD is short on room, the tally sits at the top of the sidebar's Ecosystem panel instead, and that panel now leads the tablet row so the tally is in view.

What is verified, and what is not

  • ✅ typecheck, 158 tests, build
  • ✅ Headless Chrome with failures injected through the CDP Fetch domain, at 1440×900 and 375×812, with screenshots of each state:
    • the registry failing, then recovering on the third retry
    • every health request failing (stale at 150 s), then recovering
    • Signal Map returning 503 {status:"down"}, and Passport degraded at the same time
    • AO's API unreachable
    • AO stats failing while its signals succeed
    • a tab hidden for 5 minutes, then shown
  • ✅ The layout pass also covered 1100×800, 900×800 and 768×1024, with the longest plausible names
  • ⚠️ Known leftovers, measured and not fixed here:
    • at 960×600 and 800×600, the second line of names is cut off at the tablet panel's 35vh fold
    • on phones, a long label at the far edge can lose a few pixels of its name (the tag itself is kept on screen). The next PR handles label clamping.

🤖 Generated with Claude Code

The map and sidebar counted how much is instrumented, never what the
services said: a service declaring itself down left every number unchanged
and turned one 6 px dot red, colour-only, with the legend hidden on phones.
A sweep that read nothing kept the last snapshot on screen forever, green and
breathing, with no age anywhere. A failed first registry read meant ten
minutes of "Loading registry…". A streaming service whose API stopped
answering just lost its LIVE badge, and one whose stats failed kept showing
old figures beside LIVE.

- A status tally (ok / off-contract / degraded / down / unmeasured, services
  only, archived counted by their reading) and the names of non-ok services,
  in the map HUD and the sidebar; text chips on non-ok rows, a DOWN/DEGRADED
  tag on the body's label, and the counts in document.title.
- The reading's age ("health checked HH:MM:SS") in the HUD and tooltip. After
  150 s without a landed sweep the reading is stale: bodies dim and stop
  breathing, the tally says "as of HH:MM", colours are kept. Time spent in a
  hidden tab, or a resumed sweep still in flight, does not flash stale.
- The registry retries at 5 s, 15 s, 30 s, then 60 s until it first loads,
  and the map and sidebar say "unreachable — retrying" instead of loading.
- Service I/O cards show CONNECTING / LIVE / OFFLINE, and figures from a
  stats read that failed this poll show "—" with when they were last read.
- "Signals ingested this session" starts from each origin's first read, so
  it no longer depends on whether the first poll beats the first frame.

Counting and wording live in a Phaser-free src/ui/ecosystem-status.ts with
tests.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@mikim
mikim changed the base branch from claude/network-cpu-diet to main September 22, 2026 03:23
@mikim
mikim merged commit b228468 into main Sep 22, 2026
1 check passed
@mikim
mikim deleted the claude/health-honesty branch September 22, 2026 03:25
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