docs(android): record M1 (connect & browse) landed

Mark M0+M1 done in the build-progress header and next up M2. Summarize
the M1 functional pass (first-run connect, runtime base URL + host
interceptor, layered ApiResult stack, brand-seeded theming, public
content/wiki/pages/contact screens) and record the deliberate
hand-written-vs-openapi-generated API-client deviation from §2 and its
rationale (swagger-autogen schemas are meta-descriptive, not
codegen-clean). Tracks RunicGateway/Android-app#6.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NgyHnrNa8WwG3doxvxjuCr
This commit is contained in:
2026-07-19 17:11:15 -05:00
parent 061fbee8fc
commit eda817e6f3

View File

@@ -1,6 +1,6 @@
# Android App — Plan
Status: **M0 repo scaffold landed; M1 (functional Kotlin pass) next.** This document is the
Status: **M0 + M1 landed; M2 (public shard + SSE) next.** This document is the
design contract for the `RunicGateway/Android-app` repo. It was written before implementation so the
API changes it depends on could be landed in `website/` and `docs/` first. The authoritative API
reference is the committed OpenAPI spec at `website/server/swagger/swagger-output.json` (regenerated
@@ -10,7 +10,30 @@ via `npm run swagger`).
Gradle 8.7 wrapper + AGP 8.6.1 / Kotlin 2.0.20, JDK 17, minSdk 29 / compile-target 35,
`applicationId com.runicgateway.app`; a version catalog pinning the full §2 stack; a Compose + Hilt
single-activity skeleton (externalized strings, adaptive icon); and CI (`pr-checks.yml`
`./gradlew lint test assembleDebug`). **M1M4 (functional pass) and M5 (design pass) remain.**
`./gradlew lint test assembleDebug`).
**M1 — connect & browse** (2026-07-19, `RunicGateway/Android-app#6`, functional Kotlin pass): the
first-run base-URL connect flow (probe `GET /public/status`, verify the backend's version identity,
persist to DataStore; HTTPS-only in release, HTTP allowed in debug; Settings → Server hard reset);
a runtime-selected base URL via a sentinel-host Retrofit + `HostSelectionInterceptor` (the host is
**not** compiled in) plus a `UserAgentInterceptor` past the scanner guard (§8); the layered
`screen → ViewModel → repository → PublicApi → DTO` stack returning a typed `ApiResult`
(`Ok`/`HttpError`/`NetworkError`) for graceful degradation (§7); brand-seeded Material 3 theming from
`/public/settings`; and functional Compose screens for Home/Status, News (+ post detail), Wiki
(+ detail), CMS pages (block renderer: `heading/rich_text/image/quote/cta/divider/two_column`), and
the contact form, under one declarative navigation drawer (§5). JVM unit tests cover URL
normalization, host rewriting, `ApiResult`/`UiState` mapping, and brand-color parsing.
> **API-client deviation from §2 (recorded):** DTOs + the Retrofit interface are **hand-written and
> spec-aligned**, not `openapi-generator` output. The committed `swagger-output.json` is produced by
> **swagger-autogen**, whose component schemas are meta-descriptive (nested `{type, example}`
> wrappers) rather than codegen-clean OpenAPI models, so a generator would emit unusable DTOs. The
> hand-authored client is the "checked-in generated module" §2 already allows; shapes were matched
> against the website controllers/models and every DTO ignores unknown keys (additive fields are
> safe). True codegen would first require authoring the spec's component schemas as real OpenAPI
> models.
**M2M4 (functional pass) and M5 (design pass) remain.**
**Prerequisite progress (§8):** all v1 prerequisites are **done** (2026-07-19) — ✅ password reset
(item 2; website#75 + docs#8), ✅ role-agnostic `/auth/me/*` self surface (item 1; website#76 + docs#10),