feat: the first pages, and what a browser walk found behind them #4
Reference in New Issue
Block a user
No description provided.
Delete Branch "feat/phase-4-first-pages"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Phase 4 of
docs/modules/rust/PLAN.md— recorded there as §17 (docs#256). One repo, and the first phase whose deliverable is something a visitor looks at.What it adds
/rustis the server list and the module's landing page (D12). Registered with an empty path; core renders that as the namespace root./rust/servers/:idhangs beneath it.site.footer.statusfilled with a live count (D15), linking to/rust.GET …/servers/:id, the one route under that path that can say a server does not exist — every other one answers an empty list for an id nobody configured, which is a perfectly good answer to its own question.capabilitiesgrows to what the pages serve, for phase 5's Android leg to feature-detect against.Nothing on these pages calls a game server. Every field comes from this module's own tables, which is exactly what the criterion is about: the site renders the last thing each server said while every server is off.
Four defects the walk found — two of them already shipped
Walked in a browser against the live rig: a real Rust server behind the real sidecar, plus a second, deliberately unreachable server carrying a fixture shaped as the plugin emits.
putState— the whole-row write — with two fields when nothing answered, sohostname,level,seed,world_sizeandwipe_idwent NULL the first time a host rebooted. The page then read "Offline" with nothing beside it: not "here is what we know" but "we have never heard of it", which defeats this phase's criterion exactly.markUnreachablemoves three columns and mentions no others.updated_atis when we wrote the row, and a failed poll writes it too — so an offline server claimed it had reported just now, every 30 seconds, for as long as it stayed down.last_seen_atis new and only a frame moves it.Plus a fifth, smaller: the Online tab listed players under a header reading
Offlinewith nothing saying which it was.Notes for the reviewer
useAsynccannot poll — it blanks its data on every dependency change, so a 20s refresh built on it would clear the killfeed and re-fill it four times a minute.hooks/usePolled.jsis the module's own: invisible when it succeeds, keeps the rows and reports the error when it fails.SiteFooterinsidePublicLayout, and every public page rendersPublicLayoutitself, so that component mounts once per public page view. It renders nothing until it has an answer, nothing at all if the request fails, and never polls.server/test/refresh.test.jsasserts against the SQL rather than a round trip, because the defect is about which columns a statement names.Checks
npm test(server 101 / client 39),check:imports,check:bundle,check:swagger,check:externalsand a locally regeneratedroutes.manifest.jsonagainst the pinned core all green. The chunk's identity check passed in the browser with a clean console.🤖 Generated with Claude Code
https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4
Phase 4. `/rust` is the server list and the module's landing page (D12); `/rust/servers/:id` is one server with four tabs — feed, leaderboard, who is on, wipes (D13). Everything selectable lives in the URL, so any view of the page is a link. The feed and the presence list poll every twenty seconds while the tab is visible and not at all when it is not (D14); the leaderboard and the wipe list load once. `site.footer.status` is filled with a live server and player count (D15). Nothing on these pages calls a game server. Every field comes from this module's own tables, which is what the phase criterion is about: the site renders the last thing each server said while every server is off. Walking that criterion in a browser against a live rig found four defects, two of them already shipped in phase 3: * An unreachable refresh called `putState` — the whole-row write — with two fields, so a host that rebooted lost its hostname, map, size, seed and wipe id. The list then read "Offline" with nothing beside it, which is not "here is what we know" but "we have never heard of it". `markUnreachable` now moves three columns and mentions no others. * "Last reported" read `updated_at`, which a FAILED poll writes too — so an offline server claimed it had reported just now, every thirty seconds, for as long as it stayed down. `last_seen_at` is the new column, moved only by a frame that arrived. * Feed rows showed a bare time of day, so three events from six weeks ago all read as this afternoon once the feed was filtered to a past wipe. * `/rust/servers/typo` rendered core's ErrorState under its own heading and read "No such server / Something went wrong", sending a reader who mistyped a URL looking for an outage. Also: a detail route (`GET …/servers/:id`), because it is the only route under that path that can say a server does not exist — the other four answer an empty list for an id nobody configured, and each of those is a good answer to its own question. `useAsync` cannot poll: it blanks its data on every dependency change, so a twenty-second refresh built on it would clear the killfeed and re-fill it four times a minute. `hooks/usePolled.js` is the module's own, invisible when it succeeds and keeping the rows when it fails. The client test fake was *nearly* core — it prefixed routes without stripping the trailing separator, so the first module to register an index route failed the nav check for a link that works in a browser. It now copies core's line character for character. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4