docs(android): M12 phase 0 as landed, and phase 7 cancelled #112

Merged
whitlocktech merged 1 commits from docs/android-theming-nav-phase-0 into edge 2026-08-08 07:01:57 +00:00
Member

What & why

Two records, one milestone. Code pair: Android-app#33.

Phase 7 is cancelled

Reading nav_admin / nav_player. §6.4 already made the case against it and the phase was scheduled last and marked optional precisely so the call could be taken on its merits with the rest of the milestone working. Taking it now, before phases 1–6 make it more expensive to unpick:

The two authenticated navs reach four app rows between them — /player, /account, /admin, /admin/moderation. The website sidebar's other ~18 rows are admin configuration the app deliberately excludes (M10/M11), and two of the app's four staff entries are aggregates with no single web row to be renamed from. Four rows does not pay for a new authenticated fetch, a session-keyed cache and its teardown on sign-out.

Consequences, all of them wanted:

  • the app makes no authenticated settings call at allGET /public/settings is the whole lifecycle, and there is nothing session-keyed to tear down;
  • the player and staff drawer rows keep their coded @StringRes labels, so those labels stay localized. That is the one thing given up, and for four rows the trade reads better in this direction;
  • §6.4's label-and-hidden-only rule and the label: String?-beside-labelRes mechanism still ship, because phase 5 needs both for the public nav. Nothing in phases 0–6 was scaffolding for phase 7.

Four places are amended so the doc does not contradict itself: §3's endpoint table row, §4's locked decision, §5.5's lifecycle paragraph, and the §8 table — plus a standing "Phase 7, cancelled" note that keeps the reasoning for anyone who revives it.

Phase 0 as landed

The contract and the appearance store, shipped as Android-app#33. Its notes record two departures from what §8 specified, both in service of §2's forgiving-on-read rule:

  • theme is a raw JsonElement, not Map<String, String>?. The typed map is what the contract says — a closed set of string-valued tokens, validated server-side on write. But kotlinx.serialization fails the decode of the whole object on a value of an unexpected kind, and theme shares its payload with brand and push: one odd token would have blanked the branding and dropped the push relay URL. Coerced field-by-field instead, so a bad token costs exactly itself.
  • A failed refresh keeps the last good appearance rather than falling back to NONE. §5.5 said "best-effort"; the distinction it did not draw is that a moment of no connectivity on resume must not repaint a themed shard back to the shipped defaults. Only the initial load can produce NONE.

The rest of the notes are boundary decisions worth having on the record: the second-stage parse stops at "is this a plain object" (so no half-built nav model ships in phase 0), SiteAppearance.NONE is the shipped app and three different failures resolve to it, the resume refresh lives in MainActivity beside the theme rather than in RunicApp, and RunicApp still takes brand: BrandDto? — which is what makes the phase's diff provably invisible.

PLAN.md §9's M12 entry is updated for both: eight phases now, not nine, with phase 0 marked landed.

How it was tested

Documentation only — nothing to build or run. Both records were checked against what actually shipped rather than what was planned: the §8 table, §3, §4 and §5.5 were re-read for statements the cancellation makes false, and the "as landed" notes were written from the merged diff (the JsonElement modeling, the refresh-keeps-last-good branch, the MainActivity resume effect, and the unchanged RunicApp signature), not from the plan.

Checklist

  • I have read CONTRIBUTING.md.
  • The change builds and existing tests/checks pass locally. (Markdown only.)
  • I have added or updated tests/docs where it makes sense. (This PR is the docs.)
  • My commits are reasonably scoped with clear messages.

AI-assisted contributions (required)

  • AI tools were used. Tool(s): Claude Code (Opus 5). I have reviewed and understand every change, and take responsibility for it. AI-authored commits are marked with a Co-Authored-By trailer.

License

  • I agree that my contribution is licensed under this project's license (GNU GPL v3.0 or later), and I have the right to contribute it.
## What & why Two records, one milestone. Code pair: **Android-app#33**. ### Phase 7 is cancelled Reading `nav_admin` / `nav_player`. §6.4 already made the case against it and the phase was scheduled **last and marked optional** precisely so the call could be taken on its merits with the rest of the milestone working. Taking it now, before phases 1–6 make it more expensive to unpick: The two authenticated navs reach **four app rows** between them — `/player`, `/account`, `/admin`, `/admin/moderation`. The website sidebar's other ~18 rows are admin *configuration* the app deliberately excludes (M10/M11), and two of the app's four staff entries are aggregates with no single web row to be renamed from. Four rows does not pay for a new authenticated fetch, a session-keyed cache and its teardown on sign-out. Consequences, all of them wanted: - the app makes **no authenticated settings call at all** — `GET /public/settings` is the whole lifecycle, and there is nothing session-keyed to tear down; - the player and staff drawer rows keep their coded `@StringRes` labels, so those labels stay **localized**. That is the one thing given up, and for four rows the trade reads better in this direction; - §6.4's label-and-hidden-only rule and the `label: String?`-beside-`labelRes` mechanism still ship, because **phase 5 needs both** for the public nav. Nothing in phases 0–6 was scaffolding for phase 7. Four places are amended so the doc does not contradict itself: §3's endpoint table row, §4's locked decision, §5.5's lifecycle paragraph, and the §8 table — plus a standing "Phase 7, cancelled" note that keeps the reasoning for anyone who revives it. ### Phase 0 as landed The contract and the appearance store, shipped as Android-app#33. Its notes record **two departures from what §8 specified**, both in service of §2's forgiving-on-read rule: - **`theme` is a raw `JsonElement`, not `Map<String, String>?`.** The typed map is what the contract says — a closed set of string-valued tokens, validated server-side on write. But `kotlinx.serialization` fails the decode of the *whole* object on a value of an unexpected kind, and `theme` shares its payload with `brand` and `push`: one odd token would have blanked the branding and dropped the push relay URL. Coerced field-by-field instead, so a bad token costs exactly itself. - **A failed *refresh* keeps the last good appearance** rather than falling back to `NONE`. §5.5 said "best-effort"; the distinction it did not draw is that a moment of no connectivity on resume must not repaint a themed shard back to the shipped defaults. Only the initial load can produce `NONE`. The rest of the notes are boundary decisions worth having on the record: the second-stage parse stops at "is this a plain object" (so no half-built nav model ships in phase 0), `SiteAppearance.NONE` is the shipped app and three different failures resolve to it, the resume refresh lives in `MainActivity` beside the theme rather than in `RunicApp`, and `RunicApp` still takes `brand: BrandDto?` — which is what makes the phase's diff provably invisible. `PLAN.md` §9's M12 entry is updated for both: eight phases now, not nine, with phase 0 marked landed. ## How it was tested Documentation only — nothing to build or run. Both records were checked against what actually shipped rather than what was planned: the §8 table, §3, §4 and §5.5 were re-read for statements the cancellation makes false, and the "as landed" notes were written from the merged diff (the `JsonElement` modeling, the refresh-keeps-last-good branch, the `MainActivity` resume effect, and the unchanged `RunicApp` signature), not from the plan. ## Checklist - [x] I have read [CONTRIBUTING.md](CONTRIBUTING.md). - [x] The change builds and existing tests/checks pass locally. *(Markdown only.)* - [x] I have added or updated tests/docs where it makes sense. *(This PR is the docs.)* - [x] My commits are reasonably scoped with clear messages. ## AI-assisted contributions (required) - [x] AI tools were used. Tool(s): `Claude Code (Opus 5)`. I have reviewed and understand every change, and take responsibility for it. AI-authored commits are marked with a `Co-Authored-By` trailer. ## License - [x] I agree that my contribution is licensed under this project's license (**GNU GPL v3.0 or later**), and I have the right to contribute it.
wtclaude added 1 commit 2026-08-08 06:59:20 +00:00
Two records, one milestone.

Phase 7 (reading nav_admin / nav_player) is cancelled. Section 6.4 already
made the case against it and scheduled it last so the call could be taken on
its merits with the rest working: the two authenticated navs reach four app
rows between them, which does not pay for a new authenticated fetch, a
session-keyed cache and its teardown. The app therefore makes no authenticated
settings call at all, and the player and staff drawer rows keep their coded --
and so localized -- labels, which is the one thing given up. Section 4's locked
decision, section 3's endpoint row and section 5.5's lifecycle paragraph are
amended to match so the doc does not contradict itself, and section 8 gains
the standing "Phase 7, cancelled" note.

Phase 0 (the contract and the appearance store) landed as Android-app#33, and
its "as landed" notes record two departures from what section 8 specified:

- `theme` is modeled as a raw JsonElement rather than Map<String,String>?.
  kotlinx fails the decode of the whole object on a value of an unexpected
  kind, and `theme` shares its payload with `brand` and `push` -- so one odd
  token would have blanked the branding and dropped the push relay URL, the
  opposite of section 2. It is coerced field-by-field instead.
- A failed refresh keeps the last good appearance rather than falling back to
  NONE. Section 5.5 said best-effort; the distinction it did not draw is that
  a moment of no connectivity on resume must not repaint a themed shard back
  to the shipped defaults.

Co-Authored-By: Claude <noreply@anthropic.com>
whitlocktech merged commit beae4b21dd into edge 2026-08-08 07:01:57 +00:00
whitlocktech deleted branch docs/android-theming-nav-phase-0 2026-08-08 07:01:58 +00:00
Sign in to join this conversation.
No description provided.