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

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>
This commit is contained in:
2026-07-30 00:46:16 -05:00
parent 3a1bbdd165
commit 5a091157d6
2 changed files with 129 additions and 9 deletions

View File

@@ -23,6 +23,11 @@ Order 5 split in two once §8.6's cliloc dependency turned out to be a client-fo
than a parser (see §8.6). 5a is website-only and lands first so the marketplace ships with real item
names; 5b is the four-repo wire change.
**The `edge` → `main` half of order 6 is held for Android parity** (decided 2026-07-30, see §10): the
app sees none of the four new features and gates shard nav on session role alone, so merging the
cutover first would ship a shard whose app client silently disagrees with the web client about what is
public. The **bump** PRs into `edge` are unaffected and merge normally.
---
## 1. Why 3.0
@@ -921,7 +926,7 @@ not a blocker here.)
| 4 | **B/2**`points.board` (§7) | all four | new kind + `char.profile` field | ✅ Done |
| 5a | **B/3 dependency** — cliloc table (§8.6) | website, docs | none | ✅ Done |
| 5b | **B/3**`vendor.listing` (§8) | all four | new kinds | ✅ Done |
| 6 | **Cutover**`PROTOCOL_VERSION` 2→3, `edge``main` | all four | the bump | 🟨 In review |
| 6 | **Cutover**`PROTOCOL_VERSION` 2→3, `edge``main` | all four | the bump | 🟨 In review`edge``main` held for Android parity (§10) |
---
@@ -943,8 +948,24 @@ not a blocker here.)
- `npm run swagger` **and** `npm run routes:manifest` on every route-touching PR — both are committed
artifacts, and `test/routeManifest.test.js` fails on drift.
**Follow-up, not scoped for 3.0:** the Android app consumes the same public/player shard API and will
need `/public/shard/features` to hide its own nav. Track separately against `android-app/`.
**Android parity — now scoped, and it gates the cutover (decided 2026-07-30).** This was written as a
"track separately" follow-up. It was re-examined before the cutover and the gap is wider than nav
hiding: the app consumes the same public/player shard API but has **no consumer for any of the four new
features** (`ruleset`, `leaderboards`, `market`, `atlas`), no `points` block on its character sheet, no
cliloc-resolved item names (§8.6), and — the part that matters for §3 — **it gates shard navigation 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.
Two things were verified as already correct and are recorded so they are not re-derived: the app's SSE
request rides the same authenticated OkHttp client as every other call, so an app session resolves to
the same audience rung as the same account on the web; and every shard DTO in the app is
nullable-with-defaults, so field projection strips fields without a deserialization failure.
Scoped as **M11 in [`../android/PLAN.md`](../android/PLAN.md) §9**, two PRs (the visibility rules +
read-model adds, then the four screens). `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` `404`s and the app falls back to today's behavior — so
holding the cutover is a schedule decision, not a technical dependency.
---