feat(shard): the four Protocol 3.0 content screens #31

Merged
whitlocktech merged 1 commits from feat/protocol-3-screens into feat/protocol-3-visibility 2026-07-30 07:54:30 +00:00
Member

M11 Part 2 of 2 — the screens. Stacked on #30: this targets feat/protocol-3-visibility, so the diff shows only this part. Merge #30 first, then retarget this to main.

Every screen hangs on the visibility plumbing #30 added: each is hidden from the menu when the shard doesn't publish its feature, and each self-reports "not available here" from its own 404/403, so a deep link to a gated surface still lands on an honest answer rather than a generic error.

Rules — /public/shard/ruleset

A null body is a success state, not the feature being off: the shard is reachable and simply hasn't emitted world.ruleset yet. The screen tells the two apart, since conflating them would tell an operator their config is broken when the plugin just hasn't swept.

Blocks render only when published — an omitted block means the system is off, not unknown, so an empty section would assert something false. Skill caps are converted out of tenths; the raw 1000 reads as a shard with ten times the usual limit. Live via world.ruleset, which the shard re-emits on every sidecar reconnect, so a restart with edited config updates an open screen.

Leaderboards — /public/shard/points

Boards order most-contested first, live via points.board merged through the existing LiveBoard. maxPoints: 0 is uncapped, so no cap line is drawn and nothing divides by it; a cliloc-named board (nameString: null, the usual case on a real shard) falls back to the humanised PointsType key. A nameless rank is a valid row — the character name is the feature's one admin-configurable field, so a shard can publish standings without naming who holds them.

There is deliberately no points.remove handling: the system set is fixed for a shard build, the same argument city.update makes.

Market — /public/shard/market + /meta + /vendors/:serial

Not live, on purpose. The market feature ships with its SSE fan-out disabled — a firehose of full vendor inventories would be the site's biggest bandwidth consumer — so this is a plain paginated read that never waits on a frame. It's searched on submit rather than per keystroke because it's the site's first genuinely rate-limited public endpoint.

The staleness line is required, not decoration: the shard sweeps vendors round-robin, so a price can legitimately be a full cycle old, and a screen implying live prices sends someone to an item that sold twenty minutes ago.

The vendor screen is the only surface that can render the two states a result list can't — a truncated shop ("showing 250 of 3,104"), and a gated location, which it states as a real answer rather than leaving a blank coordinate. The nesting is what makes that clean: one admin rule takes the facet, coordinates, region and house together, so there's no half-populated location to render.

Atlas — /public/atlas/creatures[/:slug]

Static shard content, so it stays readable while the shard is down — but it is site-mode gated, unlike /shard/*. Rows lead with the server's placement label ("Despise, Felucca"), which is the point-in-rect transform the whole feature exists for; the fallback chain ends at raw coordinates, which is honest for the ~17% of stock spawns that resolve to no named place.

Respawn delays are read as seconds — the unit the server's parser normalises XmlSpawner's mixed minutes/seconds into. Facet filter options are discovered from the shard's own data and a filtered page doesn't narrow them; nothing here names a facet, since a shard may add, replace or rename them.

Verified

  • ./gradlew testDebugUnitTest336 tests, 0 failures (32 new). Covers the tenths conversion, uncapped-vs-zero-cap, the seconds unit, gated-location-is-null, stack counts, board ordering, live-frame merge, and each screen's 404/403 → "not available".
  • ./gradlew lint — clean.

The five-rung on-device walk (v3.md §11) runs against a local website on the cutover branch before the cutover merges — that's the remaining gate, and it's why the cutover is being held.

  • AI-assisted: authored with Claude Code (Claude Opus 5); commits carry a Co-Authored-By trailer.

🤖 Generated with Claude Code

https://claude.ai/code/session_01U7CBg11prhLimL9iHSX1bP

M11 Part 2 of 2 — the screens. **Stacked on #30**: this targets `feat/protocol-3-visibility`, so the diff shows only this part. Merge #30 first, then retarget this to `main`. Every screen hangs on the visibility plumbing #30 added: each is hidden from the menu when the shard doesn't publish its feature, and each self-reports "not available here" from its own `404`/`403`, so a deep link to a gated surface still lands on an honest answer rather than a generic error. ## Rules — `/public/shard/ruleset` A **`null` body is a success state**, not the feature being off: the shard is reachable and simply hasn't emitted `world.ruleset` yet. The screen tells the two apart, since conflating them would tell an operator their config is broken when the plugin just hasn't swept. Blocks render only when published — an omitted block means the system is **off**, not unknown, so an empty section would assert something false. Skill caps are converted out of tenths; the raw `1000` reads as a shard with ten times the usual limit. Live via `world.ruleset`, which the shard re-emits on every sidecar reconnect, so a restart with edited config updates an open screen. ## Leaderboards — `/public/shard/points` Boards order most-contested first, live via `points.board` merged through the existing `LiveBoard`. `maxPoints: 0` is **uncapped**, so no cap line is drawn and nothing divides by it; a cliloc-named board (`nameString: null`, the usual case on a real shard) falls back to the humanised `PointsType` key. A **nameless rank is a valid row** — the character name is the feature's one admin-configurable field, so a shard can publish standings without naming who holds them. There is deliberately no `points.remove` handling: the system set is fixed for a shard build, the same argument `city.update` makes. ## Market — `/public/shard/market` + `/meta` + `/vendors/:serial` **Not live, on purpose.** The `market` feature ships with its SSE fan-out disabled — a firehose of full vendor inventories would be the site's biggest bandwidth consumer — so this is a plain paginated read that never waits on a frame. It's searched **on submit rather than per keystroke** because it's the site's first genuinely rate-limited public endpoint. The staleness line is **required, not decoration**: the shard sweeps vendors round-robin, so a price can legitimately be a full cycle old, and a screen implying live prices sends someone to an item that sold twenty minutes ago. The vendor screen is the only surface that can render the two states a result list can't — a `truncated` shop ("showing 250 of 3,104"), and a **gated location**, which it states as a real answer rather than leaving a blank coordinate. The nesting is what makes that clean: one admin rule takes the facet, coordinates, region and house together, so there's no half-populated location to render. ## Atlas — `/public/atlas/creatures[/:slug]` Static shard *content*, so it stays readable while the shard is down — but it **is** site-mode gated, unlike `/shard/*`. Rows lead with the server's placement label ("Despise, Felucca"), which is the point-in-rect transform the whole feature exists for; the fallback chain ends at raw coordinates, which is honest for the ~17% of stock spawns that resolve to no named place. Respawn delays are read as **seconds** — the unit the server's parser normalises XmlSpawner's mixed minutes/seconds into. Facet filter options are **discovered from the shard's own data** and a filtered page doesn't narrow them; nothing here names a facet, since a shard may add, replace or rename them. ## Verified - `./gradlew testDebugUnitTest` — **336 tests, 0 failures** (32 new). Covers the tenths conversion, uncapped-vs-zero-cap, the seconds unit, gated-location-is-null, stack counts, board ordering, live-frame merge, and each screen's `404`/`403` → "not available". - `./gradlew lint` — clean. The five-rung on-device walk (`v3.md` §11) runs against a local website on the cutover branch before the cutover merges — that's the remaining gate, and it's why the cutover is being held. - [x] AI-assisted: authored with **Claude Code** (Claude Opus 5); commits carry a `Co-Authored-By` trailer. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01U7CBg11prhLimL9iHSX1bP
wtclaude added 1 commit 2026-07-30 07:51:16 +00:00
M11 Part 2 (docs/android/PLAN.md §9), on the visibility plumbing Part 1 added.
Each screen hides from the menu when the shard doesn't publish its feature, and
self-reports "not available here" from its own 404/403 so a deep link still
lands on an honest answer.

  - Rules (/public/shard/ruleset). A null body means the shard has never
    published a ruleset, which is a SUCCESS state, not the feature being off —
    the screen tells the two apart. Blocks render only when published, since an
    omitted block means the system is off rather than unknown. Skill caps are
    converted out of tenths; the raw 1000 reads as ten times the real limit.
    Live via world.ruleset, which the shard re-emits on every reconnect.
  - Leaderboards (/public/shard/points). Boards order most-contested first, live
    via points.board. maxPoints 0 is uncapped so no cap line is drawn, and a
    cliloc-named board (nameString null, the usual case) falls back to the
    humanised PointsType key. A nameless rank is a valid row: the character name
    is the feature's one admin-configurable field.
  - Market (/public/shard/market + /meta + /vendors/:serial). NOT live: the
    market feature ships with its SSE fan-out disabled, so this is a plain
    paginated read, searched on submit rather than per keystroke because it is
    the site's first rate-limited public endpoint. The staleness line is
    required, not decoration — the round-robin sweep means a price can be a full
    cycle old. The vendor screen is the only surface that can render a truncated
    shop and a gated location, the latter as a real answer rather than a blank
    coordinate.
  - Atlas (/public/atlas/creatures[/:slug]). Static shard content, so it stays
    readable while the shard is down — but site-mode gated, unlike /shard/*.
    Rows lead with the server's placement label ("Despise, Felucca"), which is
    the transform the whole feature exists for. Respawn delays are read as
    SECONDS, the unit the parser normalises XmlSpawner's mixed minutes/seconds
    into. Facet filter options are discovered from the shard's own data — nothing
    here names a facet, since a shard may add, replace or rename them.

336 unit tests pass (32 new); lint clean. The five-rung on-device walk runs
against a local website on the cutover branch before the cutover merges.

Co-Authored-By: Claude <noreply@anthropic.com>
whitlocktech merged commit 06b6b015c2 into feat/protocol-3-visibility 2026-07-30 07:54:30 +00:00
whitlocktech deleted branch feat/protocol-3-screens 2026-07-30 07:54:31 +00:00
Sign in to join this conversation.
No description provided.