docs(android): scope M11 — Protocol 3.0 shard parity for the app #74

Merged
whitlocktech merged 1 commits from docs/android-v3-parity into edge 2026-07-30 07:23:08 +00:00
Member

Why

link/v3.md §10 carried Android parity as a "track separately" follow-up, described as needing /public/shard/features to hide its own nav. Re-examined before the edgemain cutover, the gap is wider than nav hiding.

The app cannot see any of the four features v3 added — no reference to ruleset, leaderboards, market or atlas anywhere in the Kotlin tree — plus two carry-overs: no points block on the character sheet (§7.3) and no cliloc-resolved item names (§8.6), so equipment still renders as a layer or a bare id where the web sheet shows real names.

And the part that matters for §3: Menu.kt gates shard navigation on session role alone. An admin who disables a feature or raises its audience leaves the app rendering entries that 404/403 into a generic error screen where the web client hides them. requireFeature's two statuses mean different things (404 = disabled and deliberately not disclosed, 403 = below the audience) and the app distinguishes neither.

What this adds

android/PLAN.md §9 M11 — Protocol 3.0 shard parity, in two PRs:

  • Part 1 — the visibility rules + read-model adds. The /public/shard/features consumer (mirroring the web client's per-viewer session cache), MenuEntry.feature beside the existing access, the 404/403/503 split, char.profile.points, and equipment[].clilocName / titles.rewardResolved.
  • Part 2 — the four screens. Rules, Leaderboards, Market (+ vendor detail), Atlas (+ creature detail), each hidden by its feature name.

The traps a real shard exposes are recorded inline rather than left to be rediscovered: uncapped maxPoints: 0 being the common case, nameString usually null so humanising the system key is the primary display path, skill caps in tenths, the market staleness banner being required rather than decorative, the market SSE fan-out being off by default, atlas delays in seconds, and points-as-count vs spawners-as-list.

Also splits §5's guideline: session role is no longer the only gate on a shard surface, and the in-flight/failed lookup deliberately fails open — the same call the web client makes, because the server gates regardless and a nav that flickers in on every load is worse than a link that briefly 403s.

Two things verified as already correct

Recorded so they are not re-derived later:

  • The app's SSE request is built from the authenticated OkHttp client, so an app session resolves to the same audience rung as the same account on the web. §3.6's per-connection filtering already works for the app.
  • Every shard DTO is nullable-with-defaults, so field projection strips fields without a MissingFieldException.

Cutover sequencing

edgemain is held until both app PRs land, so web and app surface the same shard on the same day. The bump PRs into edge (link#20, website#117, docs#72) are unaffected.

Neither app PR is coupled to the merge order: against a pre-v3 website every new route and /public/shard/features 404s and each consumer falls back to today's behavior, and the app declares no protocol version and never talks to the sidecar. Holding the cutover is a schedule decision, not a technical dependency — recorded that way in §10 so the reason survives.

Scope note

Excluded, in the same class as M10's exclusions: the admin configuration panels (Shard Visibility, Spawn Atlas, Cliloc import), alongside the hero/CMS editor, Discord-bot config, uo-link config and OAuth-provider setup.


Docs only — no code, no schema, no routes.

  • 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

## Why `link/v3.md` §10 carried Android parity as a "track separately" follow-up, described as needing `/public/shard/features` to hide its own nav. Re-examined before the `edge` → `main` cutover, the gap is wider than nav hiding. The app **cannot see any of the four features v3 added** — no reference to `ruleset`, `leaderboards`, `market` or `atlas` anywhere in the Kotlin tree — plus two carry-overs: no `points` block on the character sheet (§7.3) and no cliloc-resolved item names (§8.6), so equipment still renders as a layer or a bare id where the web sheet shows real names. And the part that matters for §3: **`Menu.kt` gates shard navigation on session role alone.** An admin who disables a feature or raises its audience leaves the app rendering entries that `404`/`403` into a generic error screen where the web client hides them. `requireFeature`'s two statuses mean different things (`404` = disabled and deliberately not disclosed, `403` = below the audience) and the app distinguishes neither. ## What this adds `android/PLAN.md` §9 **M11 — Protocol 3.0 shard parity**, in two PRs: - **Part 1 — the visibility rules + read-model adds.** The `/public/shard/features` consumer (mirroring the web client's per-viewer session cache), `MenuEntry.feature` beside the existing `access`, the `404`/`403`/`503` split, `char.profile.points`, and `equipment[].clilocName` / `titles.rewardResolved`. - **Part 2 — the four screens.** Rules, Leaderboards, Market (+ vendor detail), Atlas (+ creature detail), each hidden by its feature name. The traps a real shard exposes are recorded inline rather than left to be rediscovered: uncapped `maxPoints: 0` being the *common* case, `nameString` usually null so humanising the `system` key is the primary display path, skill caps in tenths, the market staleness banner being required rather than decorative, the `market` SSE fan-out being **off by default**, atlas delays in seconds, and `points`-as-count vs `spawners`-as-list. Also splits §5's guideline: session role is no longer the only gate on a shard surface, and the in-flight/failed lookup deliberately **fails open** — the same call the web client makes, because the server gates regardless and a nav that flickers in on every load is worse than a link that briefly `403`s. ## Two things verified as already correct Recorded so they are not re-derived later: - The app's SSE request is built from the **authenticated** OkHttp client, so an app session resolves to the same audience rung as the same account on the web. §3.6's per-connection filtering already works for the app. - Every shard DTO is nullable-with-defaults, so field projection strips fields without a `MissingFieldException`. ## Cutover sequencing `edge` → `main` is **held until both app PRs land**, so web and app surface the same shard on the same day. The bump PRs into `edge` (link#20, website#117, docs#72) are unaffected. Neither app PR is coupled to the merge order: against a pre-v3 website every new route and `/public/shard/features` `404`s and each consumer falls back to today's behavior, and the app declares no protocol version and never talks to the sidecar. Holding the cutover is a schedule decision, not a technical dependency — recorded that way in §10 so the reason survives. ## Scope note Excluded, in the same class as M10's exclusions: the admin *configuration* panels (Shard Visibility, Spawn Atlas, Cliloc import), alongside the hero/CMS editor, Discord-bot config, uo-link config and OAuth-provider setup. --- Docs only — no code, no schema, no routes. - [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 05:46:58 +00:00
The v3 work added four shard features and an admin-configurable visibility
framework the Android client knows nothing about. v3.md §10 deferred the app
side as a follow-up; re-examining it before the cutover found the gap is wider
than nav hiding:

  - no consumer for any of ruleset / leaderboards / market / atlas,
  - no `points` block on the character sheet (§7.3),
  - no cliloc-resolved item names (§8.6), and
  - shard nav gated on session role alone, so an admin who disables a feature
    or raises its audience leaves the app rendering entries that 404/403 into a
    generic error where the web client hides them.

Scoped as PLAN.md §9 M11 in two PRs (the visibility rules + read-model adds,
then the four screens), with the traps a real shard exposes recorded inline:
uncapped `maxPoints: 0`, cliloc-named boards with a null `nameString`, skill
caps in tenths, the required market staleness banner, the market stream being
off by default, atlas delays in seconds, and `points`-count vs `spawners`-list.

edge → main is held until both land so web and app surface the same shard on
the same day. Neither PR is coupled to the merge order — on a pre-v3 website
every new route and /public/shard/features 404s and the app falls back to
today's behavior — so holding the cutover is a schedule decision, not a
technical dependency.

Also records two things verified as already correct, so they are not
re-derived: the app's SSE request rides the authenticated client (same audience
rung as the same account on web), and every shard DTO is nullable-with-defaults
(field projection cannot cause a decode failure).

Co-Authored-By: Claude <noreply@anthropic.com>
whitlocktech merged commit 45fb4a3f15 into edge 2026-07-30 07:23:08 +00:00
whitlocktech deleted branch docs/android-v3-parity 2026-07-30 07:23:09 +00:00
Sign in to join this conversation.
No description provided.