docs(android): add design-pass workflow to app PLAN #7

Merged
whitlocktech merged 1 commits from docs/android-design-pass-workflow into main 2026-07-19 08:36:25 +00:00

View File

@@ -82,6 +82,17 @@ runs `openapi-generator` against the committed spec. Endpoints that return
service (Retrofit) → DTO`. Repositories expose `Result`-like sealed types so the UI degrades service (Retrofit) → DTO`. Repositories expose `Result`-like sealed types so the UI degrades
gracefully (see §7). gracefully (see §7).
### 2.1 Build workflow: Kotlin first, then design-led UI
The app is built in two passes. **First**, the functional Kotlin is written — the layering above with
placeholder/functional Compose screens: navigation, ViewModels, repositories, the generated API
client, auth/token handling, and every screen wired to its endpoints and working end-to-end. **Then**,
once that Kotlin code is done, **Claude Design produces the front-end design** for the app, and
**Claude Code implements the final UI (Compose screens, theming, components) according to that
design.** The design pass restyles and refines the already-working screens; it does not change the
architecture, data flow, or endpoint contracts established in the first pass. Keeping strings
externalized and branding data-driven (§2, §3) from the start is what lets the design pass reskin
freely without touching logic.
--- ---
## 3. Base URL: first-run + settings ## 3. Base URL: first-run + settings
@@ -294,7 +305,7 @@ maintenance cost. Reserve v2 for a real breaking re-shape if one ever arises.
stay website-handled and the app hands off to them (§4.2). This is a deliberate scope reduction. stay website-handled and the app hands off to them (§4.2). This is a deliberate scope reduction.
3. **Push notifications** — see §11. Additive v1 endpoints under `/auth/me/devices*` and 3. **Push notifications** — see §11. Additive v1 endpoints under `/auth/me/devices*` and
`/auth/me/notifications*`, plus a **self-hosted `ntfy` service added to `website/docker-compose.yml`** `/auth/me/notifications*`, plus a **self-hosted `ntfy` service added to `website/docker-compose.yml`**
with fully declarative, zero-interaction config. Not required for the first release (M6, not M1M5). with fully declarative, zero-interaction config. Not required for the first release (M7, not M1M6).
4. **Version/health surfacing** — ensure `/public/status` (or a light `/public/version`) exposes 4. **Version/health surfacing** — ensure `/public/status` (or a light `/public/version`) exposes
enough for the app's first-run probe and version-mismatch guard. enough for the app's first-run probe and version-mismatch guard.
5. **Docs** — update `docs/website/BACKEND_DESIGN.md` for any new/changed endpoint; keep this file and 5. **Docs** — update `docs/website/BACKEND_DESIGN.md` for any new/changed endpoint; keep this file and
@@ -309,25 +320,41 @@ No `link/` or `servuo-plugins/` changes are expected — the app is downstream o
## 9. Milestones ## 9. Milestones
**Two passes (§2.1).** M0M4 are the **functional Kotlin pass** — every screen wired to its endpoints
and working end-to-end with placeholder/functional Compose UI, no design investment yet. **M5 is the
design pass**: once the functional Kotlin is done, Claude Design produces the front-end design and
Claude Code implements the final UI to it. Polish/release, push, and Play (M6M8) follow the designed
app.
1. **M0 — Repo scaffold**: Gradle + Compose + Hilt skeleton, CI (build + lint + unit test), license 1. **M0 — Repo scaffold**: Gradle + Compose + Hilt skeleton, CI (build + lint + unit test), license
headers (GPL-3.0-or-later), CONTRIBUTING/AI-disclosure parity with the other repos. headers (GPL-3.0-or-later), CONTRIBUTING/AI-disclosure parity with the other repos.
2. **M1 — Connect & browse**: first-run base-URL flow, `/public/status`+`/public/settings` theming, 2. **M1 — Connect & browse** *(functional pass)*: first-run base-URL flow,
generated API client, public content (news/wiki/pages) + contact. No auth yet. `/public/status`+`/public/settings` theming, generated API client, public content
3. **M2 — Public shard**: shard widgets + SSE live stream with reconnect/degradation. (news/wiki/pages) + contact. No auth yet.
4. **M3Auth (§4)**: native password+TOTP login (429 handling), token storage, refresh interceptor, 3. **M2Public shard** *(functional pass)*: shard widgets + SSE live stream with
logout, `/auth/me` role re-validation, the access-level menu. Custom-Tab **hand-offs** to the website reconnect/degradation.
for register / invite / password-reset / SSO (no native screens for those). Optional biometric 4. **M3 — Auth (§4)** *(functional pass)*: native password+TOTP login (429 handling), token storage,
app-lock. *Prerequisite:* the website password-reset flow (§8) is already built. refresh interceptor, logout, `/auth/me` role re-validation, the access-level menu. Custom-Tab
5. **M4 — Player self-service & game data**: account management (via `/auth/me/*`), game-account **hand-offs** to the website for register / invite / password-reset / SSO (no native screens for
linking, own roster/characters/vendors/houses/sales — **text-only** presentation (§6.3). those). Optional biometric app-lock. *Prerequisite:* the website password-reset flow (§8) is
6. **M5 — Polish & first release**: settings (server switch = hard reset), version-mismatch guard, already built.
5. **M4 — Player self-service & game data** *(functional pass)*: account management (via
`/auth/me/*`), game-account linking, own roster/characters/vendors/houses/sales — **text-only**
presentation (§6.3).
6. **M5 — Design pass & final UI (§2.1)**: with the functional Kotlin from M1M4 working end-to-end,
**Claude Design produces the front-end design** for the app, then **Claude Code implements the final
UI to it** — Compose screens, Material 3 theming from the per-shard branding (§3), reusable
components, loading/error/empty states, the designed access-level menu. Restyles the existing
screens only; no changes to architecture, data flow, or endpoint contracts. Text-only game data
(§6.3) still holds — this is visual design of the data screens, not paperdoll art.
7. **M6 — Polish & first release**: settings (server switch = hard reset), version-mismatch guard,
release build hardening (HTTPS-only, no token logging). No offline cache in v1 (§7). **Ship v1 as a release build hardening (HTTPS-only, no token logging). No offline cache in v1 (§7). **Ship v1 as a
signed APK attached to a Gitea release** (see §10). signed APK attached to a Gitea release** (see §10).
7. **M6 — Push notifications** (post-v1): add the self-hosted `ntfy` service to 8. **M7 — Push notifications** (post-v1): add the self-hosted `ntfy` service to
`website/docker-compose.yml` (declarative, zero-interaction config), UnifiedPush integration in the `website/docker-compose.yml` (declarative, zero-interaction config), UnifiedPush integration in the
app, device registration, the subscriptions UI, and the content-free-tickle backend fan-out (see app, device registration, the subscriptions UI, and the content-free-tickle backend fan-out (see
§11). The app is built with room for this from M0 but it does not gate the first release. §11). The app is built with room for this from M0 but it does not gate the first release.
8. **M7 — Google Play**: Play Console listing, signing/upload key, and (optionally) an FCM build flavor 9. **M8 — Google Play**: Play Console listing, signing/upload key, and (optionally) an FCM build flavor
— after the direct-APK release is stable. — after the direct-APK release is stable.
--- ---
@@ -344,7 +371,7 @@ No `link/` or `servuo-plugins/` changes are expected — the app is downstream o
## 11. Push notifications (built-for, shipped post-v1) ## 11. Push notifications (built-for, shipped post-v1)
The app is architected from M0 to accommodate push, but push itself ships in M6 — it does not block the The app is architected from M0 to accommodate push, but push itself ships in M7 — it does not block the
first release. Users **opt in per stream**: nothing is pushed unless subscribed. first release. Users **opt in per stream**: nothing is pushed unless subscribed.
### Transport — UnifiedPush via self-hosted ntfy (decided) ### Transport — UnifiedPush via self-hosted ntfy (decided)
@@ -422,7 +449,7 @@ repos use), on a bare `ubuntu:latest` container.
frequent: run the job under a prebuilt Android-SDK `container:` image so nothing installs per-run.) frequent: run the job under a prebuilt Android-SDK `container:` image so nothing installs per-run.)
- **PR gate** (`.gitea/workflows/pr-checks.yml`, on PR → `main`): `./gradlew lint test assembleDebug`. - **PR gate** (`.gitea/workflows/pr-checks.yml`, on PR → `main`): `./gradlew lint test assembleDebug`.
Debug builds are auto-signed, so the gate needs no secrets. Mirrors `website/`'s pre-merge gate. Debug builds are auto-signed, so the gate needs no secrets. Mirrors `website/`'s pre-merge gate.
- **Release** (`.gitea/workflows/release.yml`, M5+): build a **signed release APK** and attach it to a - **Release** (`.gitea/workflows/release.yml`, M6+): build a **signed release APK** and attach it to a
Gitea release (mirrors `link/`'s release job). The **keystore is a base64 Gitea Actions secret** Gitea release (mirrors `link/`'s release job). The **keystore is a base64 Gitea Actions secret**
decoded in CI; store/key passwords are secrets. The keystore never lives in the repo. Keep the decoded in CI; store/key passwords are secrets. The keystore never lives in the repo. Keep the
signing identity stable from the first release (Play later requires consistency). signing identity stable from the first release (Play later requires consistency).
@@ -445,7 +472,7 @@ ntfy / UnifiedPush (§11).
own verified app links — confirm whether any deep-link-back is wanted at all for v1. own verified app links — confirm whether any deep-link-back is wanted at all for v1.
- ntfy: exact upstream image + pinned tag, its reverse-proxy hostname/path, and whether to add a - ntfy: exact upstream image + pinned tag, its reverse-proxy hostname/path, and whether to add a
backend publish token (optional hardening — the content-free-tickle design does not require one). backend publish token (optional hardening — the content-free-tickle design does not require one).
- FCM flavor: build it for the Play release or ship Play on UnifiedPush too? Decide at M7. - FCM flavor: build it for the Play release or ship Play on UnifiedPush too? Decide at M8.
- Deep-link / share targets for wiki pages, posts, and notification taps. - Deep-link / share targets for wiki pages, posts, and notification taps.
- iOS: none planned (this is the Android-only choice); revisit only if cross-platform is later - iOS: none planned (this is the Android-only choice); revisit only if cross-platform is later
required (would change §2 — and push, which would then favor a cross-platform transport). required (would change §2 — and push, which would then favor a cross-platform transport).