docs(website): the spawn atlas API, the admin panel, and the delay-unit trap
Docs half of website #113 (Protocol 3.0 Part C, second website PR). Carries the atlas rewrite that missed #67: that PR merged before the "derive from the tree on every boot" commit was pushed, so `edge` currently describes the build/import-artifact design that was rejected in review, not what shipped in website #112. It lands here. New in SPAWN_ATLAS.md: the six public routes and five admin ones, and three behaviours that read as bugs unless they are written down — an unreadable tree answers 200 with status "unavailable" rather than 500 (refresh reports outcomes so boot is never blocked by a bad tree, and the contract is preserved at the API), setting the ServUO path deliberately does not import, and `points` is a count while `spawners` is the list. Also the delay-unit trap: XmlSpawner stores MinDelay/MaxDelay in minutes OR seconds per record, decided by that record's own DelayInSec flag, so a `5` is five minutes on one spawner and five seconds on the next. Both are plausible respawn times, which is what makes it silent. 170 of 6,455 stock spawners are second-flagged. And PARSER_VERSION, which exists because hashing the tree alone would strand an install whose maps never change on whatever an older parser derived. BACKEND_DESIGN.md gains the routes, the router-map entry, and the parser-version rule. v3.md marks Part C done and records in 6.3 what the API half found. api-route-inventory.json refreshed from the live manifest — it had drifted to 200 routes before this PR (real count was 204) and is now 215. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01U7CBg11prhLimL9iHSX1bP
This commit is contained in:
42
link/v3.md
42
link/v3.md
@@ -13,7 +13,7 @@ Each part is marked off here as it lands on `edge`. §9 carries the same state p
|
||||
|---|---|---|---|
|
||||
| 1 | **A** — visibility framework + actor-leak fix (§3) | ✅ **Done** | website [#109](https://gitea.whitlocktech.com/RunicGateway/website/pulls/109) + [#110](https://gitea.whitlocktech.com/RunicGateway/website/pulls/110), docs [#64](https://gitea.whitlocktech.com/RunicGateway/docs/pulls/64) + [#65](https://gitea.whitlocktech.com/RunicGateway/docs/pulls/65) |
|
||||
| 2 | **B/1** — `world.ruleset` (§5) | ✅ **Done** | servuo-plugins [#3](https://gitea.whitlocktech.com/RunicGateway/servuo-plugins/pulls/3), link [#17](https://gitea.whitlocktech.com/RunicGateway/link/pulls/17), website [#111](https://gitea.whitlocktech.com/RunicGateway/website/pulls/111), docs [#66](https://gitea.whitlocktech.com/RunicGateway/docs/pulls/66) |
|
||||
| 3 | **C** — spawn atlas (§6) | 🟡 **Data pipeline done** | website [#112](https://gitea.whitlocktech.com/RunicGateway/website/pulls/112) (parsers + CLI + tables); API/client PR next |
|
||||
| 3 | **C** — spawn atlas (§6) | ✅ **Done** | website [#112](https://gitea.whitlocktech.com/RunicGateway/website/pulls/112) (parsers + CLI + tables) + [#113](https://gitea.whitlocktech.com/RunicGateway/website/pulls/113) (API + pages + admin panel), docs [#67](https://gitea.whitlocktech.com/RunicGateway/docs/pulls/67) + [#68](https://gitea.whitlocktech.com/RunicGateway/docs/pulls/68) |
|
||||
| 4 | **B/2** — `points.board` (§7) | ⬜ Not started | — |
|
||||
| 5 | **B/3** — `vendor.listing` (§8) | ⬜ Not started | — |
|
||||
| 6 | **Cutover** — `PROTOCOL_VERSION` 2→3 (§4) | ⬜ Not started | — |
|
||||
@@ -329,8 +329,10 @@ frame during verification.
|
||||
|
||||
**No plugin, no sidecar, no `Bridge.cfg` knob, no new kinds.** Not part of the v3 wire change.
|
||||
|
||||
> **Status:** data pipeline landed on `edge` — website [#112](https://gitea.whitlocktech.com/RunicGateway/website/pulls/112)
|
||||
> (parsers, build/import CLI, tables, artifact). API + client pages are the second website PR.
|
||||
> **Status:** complete on `edge` — website [#112](https://gitea.whitlocktech.com/RunicGateway/website/pulls/112)
|
||||
> (parsers, import CLI, tables) and [#113](https://gitea.whitlocktech.com/RunicGateway/website/pulls/113)
|
||||
> (the six public routes, the five admin ones, `/site/atlas` + `/site/atlas/:slug`, and the
|
||||
> Admin → Spawn Atlas panel).
|
||||
> Part C ships as **two** website PRs, not one: the parsing half is where the correctness risk
|
||||
> lives, and burying it under routes and React would have meant reviewing it in a 10k-line diff.
|
||||
> Full operator documentation: [`docs/website/SPAWN_ATLAS.md`](../website/SPAWN_ATLAS.md).
|
||||
@@ -471,6 +473,38 @@ region, 1,690 by landmark, 1,086 Wilderness).
|
||||
**One thing the design got exactly right:** the point-in-rect transform really is the reason to
|
||||
build this. "Where does a lizardman spawn?" answers *Shrines, Isamu-Jima, Yew* across three facets.
|
||||
|
||||
### 6.3 What the API/client half added
|
||||
|
||||
The second website PR built the six public routes, the five admin ones, `/site/atlas` +
|
||||
`/site/atlas/:slug`, and the Admin → Spawn Atlas panel. Three things it changed or established:
|
||||
|
||||
**1. Respawn delays were being read in the wrong unit — sometimes.** XmlSpawner writes
|
||||
`MinDelay`/`MaxDelay` in minutes and switches to seconds only when a delay does not divide into
|
||||
whole minutes, flagging that per record with `DelayInSec`
|
||||
(`XmlSpawner2.cs:7462-7480`, read back at `:6345-6358`). So a `5` means five *minutes* on one
|
||||
spawner and five *seconds* on the next, both plausible, and the pipeline stored the raw number.
|
||||
170 of 6,455 stock spawners are second-flagged — few enough to look like noise on a page and be
|
||||
believed. The parser now normalises to **seconds**, and the API and UI carry seconds throughout.
|
||||
*This is the class of bug §6.2 is a list of: the atlas still builds, it is just quietly wrong.*
|
||||
|
||||
**2. The hash gate needed a parser version, and this generalises.** Fixing the parse exposed that
|
||||
"has the tree changed?" is the wrong question on its own — an install whose maps never change would
|
||||
have kept serving the old readings forever, because the only thing compared was the tree.
|
||||
`spawnAtlasSource.PARSER_VERSION` is stored in `shard_atlas_meta` beside the source hashes, and a
|
||||
mismatch counts as drift. Any future parse correction lands on the next boot without an operator
|
||||
having to know it happened. **Bump it whenever the parser derives different data from identical
|
||||
files.**
|
||||
|
||||
**3. `points` is a count; `spawners` is the list.** The first cut of the detail route spread the
|
||||
creature row and then set `points` to the array of spawn points — the same key meaning a number on
|
||||
the search route and an array on the detail route. Renamed before it shipped, and worth recording
|
||||
because the two names are one letter apart in meaning and it reads as correct.
|
||||
|
||||
**On projection.** The `atlas` feature declares no sensitive fields, so `projectFeature` is a no-op
|
||||
on every one of these routes today. Every handler calls it anyway, per §3.6.1's rule — the point of
|
||||
the rule is that the *first* field that needs gating is covered by construction rather than by a
|
||||
retrofit nobody remembers to do.
|
||||
|
||||
---
|
||||
|
||||
## 7. Part B/2 — `points.board`
|
||||
@@ -665,7 +699,7 @@ inherently up to one full cycle old, and the UI must say so.
|
||||
|---|---|---|---|---|
|
||||
| 1 | **A** — visibility framework + actor-leak fix | website, docs | none | ✅ Done |
|
||||
| 2 | **B/1** — `world.ruleset` (§5) | all four | new kind | ✅ Done |
|
||||
| 3 | **C** — spawn atlas (§6) | website, docs | none | 🟡 Pipeline done, API/client next |
|
||||
| 3 | **C** — spawn atlas (§6) | website, docs | none | ✅ Done |
|
||||
| 4 | **B/2** — `points.board` (§7) | all four | new kind + `char.profile` field | ⬜ |
|
||||
| 5 | **B/3** — `vendor.listing` (§8) | all four | new kinds | ⬜ |
|
||||
| 6 | **Cutover** — `PROTOCOL_VERSION` 2→3, `edge` → `main` | all four | the bump | ⬜ |
|
||||
|
||||
Reference in New Issue
Block a user