Files
werkregie/doc/prs/2026-10-12-PR#52-robust-reload.md
2026-10-12 21:39:53 +02:00

154 lines
6.5 KiB
Markdown

# PR#52: The Page Loads Robustly After Waking Up and Shows a Late Relay
> **WARNING:** This document describes only the change applied in this PR.
> It may already be outdated once the next PR is merged.
> Historic PR documentation is not maintained along with new PRs; treat it as a snapshot.
## Problem
On the phone, the installed page showed only one entry under "Du bist dran" after waking up, a PR;
several sessions of Claude Code were waiting.
A manual reload showed all of them.
The cause is not certain; two candidates:
1. The page loaded during the deployment at 18:07, while server and relay restarted;
the relay `think` reported again only about 13 s later.
A relay that is late or missing is not visible in the header: its sessions are silently missing.
2. After waking up, the network was not ready; the first `load()` failed or hung, and the page kept the old state.
`load()` runs on `visibilitychange` and every 5 s; a request that hangs never ends,
and a return from the back/forward cache fires no `visibilitychange`.
## Non-Goals
- No new server endpoint and no push channel; the page keeps polling.
- No change of `relayStaleSeconds` or `relayGoneSeconds`.
- No memory of the relays in the browser; the server alone knows which relays report.
## Specification
### Feature: the page loads the current state after waking up
#### Scenario#52.01: a return to the page loads at once
… so that a page from the back/forward cache does not show an old state for 5 s.
- **Given** the user left the page, and the browser kept it in its back/forward cache
- **When** the user goes back to the page
- **Then** the page asks the server at once
##### Verified by
- Manual: see "Check in the Browser".
#### Scenario#52.02: the network coming back loads at once
… so that the page shows the current state as soon as it can.
- **Given** an open page whose last load failed
- **When** the network is back (`online`)
- **Then** the page asks the server at once
##### Verified by
- Manual: see "Check in the Browser".
#### Scenario#52.03: a failed load is retried after 1 s
… so that a network that is not ready yet after waking up costs 1 s, not 5 s.
- **Given** an open page that showed the current state
- **When** the server does not answer one load because the network is not ready
- **Then** the page asks the server again after 1 s instead of 5 s
##### Verified by
- Manual: see "Check in the Browser".
#### Scenario#52.04: a hanging request counts as failed
… so that a dead connection after waking up does not keep the old state.
- **Given** an open page
- **When** the server does not answer within 10 s
- **Then** the page aborts the request and shows "offline"
##### Verified by
- Manual: see "Check in the Browser".
#### Scenario#52.05: an older answer does not replace a newer one
… so that two loads at the same time, e.g. on `pageshow` and `visibilitychange`, do not show an old state.
- **Given** two loads in progress
- **When** the answer of the first one arrives after the answer of the second one
- **Then** the page ignores the answer of the first one
##### Verified by
- Manual: see "Check in the Browser".
### Feature: the header shows a late or missing relay
#### Scenario#52.06: a late relay shows in the header
… so that sessions that are missing or old do not go unnoticed.
- **Given** the relay `think`, silent for longer than `relayStaleSeconds`
- **When** the page shows the answer of the server
- **Then** the indicator in the header shows "think still" in amber, with the time of its last report as tooltip
##### Verified by
- [RelayRegistryTest](../../dashboard/src/test/kotlin/de/hoennig/werkregie/dashboard/relay/RelayRegistryTest.kt):
the existing tests of the `silent` status.
- Manual, for the header: see "Check in the Browser".
#### Scenario#52.07: a server without any relay shows it
… so that a lost relay state, e.g. after a deployment, does not look like a quiet day.
- **Given** a server whose relay registry is empty
- **When** the page shows the answer of the server
- **Then** the indicator in the header shows "kein Relay" in amber, and the sources list a failing "Relays"
##### Verified by
- [RelayRegistryTest](../../dashboard/src/test/kotlin/de/hoennig/werkregie/dashboard/relay/RelayRegistryTest.kt):
"an empty registry reports that no relay reports".
- Manual, for the header: see "Check in the Browser".
### Check in the Browser
The dashboard 0.32.0 ran in server mode with `relayStaleSeconds: 8` in Claude's browser pane;
`curl` posted the reports of a relay `think`, and a stand-in for `fetch` failed, hung or delayed the answers.
- Without any relay, the header showed "no relay" in amber, the overview "Relays: kein Relay meldet" (#52.07).
- With fresh reports it showed "live"; 8 s after the last report "think silent" in amber,
with "Relay think: last seen 29 s ago" as tooltip (#52.06).
- At 375 px width the header showed "THINK SILENT" in amber beside its dot, without a horizontal scroll bar.
- `online` and a `pageshow` with `persisted` each loaded at once; a `pageshow` without `persisted` did not (#52.01, #52.02).
- Both failing at once: the page loaded again after 1, 2 and 3 s, then no more; it showed "offline" and the banner (#52.03).
- A request that never ended: "live" after 9 s, "offline" after 10.5 s (#52.04).
- Of two loads at once, the first answered 1.5 s after the second; the page kept the answer of the second (#52.05).
## Solution
- `load()` aborts its request after 10 s with `AbortSignal.timeout`, where the browser has it.
- After a failure, `load()` sets one retry after 1 s, at most 3 in a row; two loads that fail at once share it.
Any load cancels a pending retry, since it takes its place.
- `visibilitychange`, `pageshow` from the back/forward cache and `online` call `loadAfterWaking()`:
it resets the retries, which a failure before sleeping may have used up.
- Each load has a number; an answer older than the one shown is ignored.
- `setLive()` shows the late relays instead of "live", in amber: a relay with `silent`, or "kein Relay".
On a phone the indicator otherwise shows its dot only; for a late relay it shows its text, too.
- `RelayRegistry.statuses()` reports a failing status `relays` with "kein Relay meldet" when it holds no relay.
So the overview and the tab *Quellen* show it, too, through the existing `connections()`.
- Version 0.32.0; PR#51 holds 0.31.0.
## Open Questions
- [ ] TODO.js-tests: the loading and the header are checked only by hand;
the repository has no runtime for tests of the page's JavaScript, and a new one needs the consent of the human.