wtclaude e10e1f1617
All checks were successful
PR Checks / android-build (pull_request) Successful in 12m7s
feat(events): the app's events screens, and the module rows that were never gated (Phase 14b)
Events Phase 14b, the app half — recorded as M13 in docs/android/PLAN.md.

Four screens on the four routes Phase 14a shipped: the public calendar, an event
page carrying `?run=`, an arc, and participation history. One drawer row for the
history, at SIGNED_IN rather than PLAYER: the route is `requireAuth` alone and
self-scoped, and the website needed two mounts for it only because `RequirePlayer`
guards `/account` there.

The prerequisite fix is the larger half. The app read `/public/modules` nowhere
and mapped every `/public/shard/features` failure to "unknown", which `canSee`
treats as visible — so on a site with no `uo` module every shard row rendered and
every one of them 404'd. Absence of an answer is not an answer of absence: a
successful module list that omits `shard` hides the rows, a failed read keeps the
last answer the host gave, and a host that has never answered leaves the gate
open. Capability and feature compose as two gates and answer different questions:
whether the module is installed (per host) and whether this shard publishes the
surface to this viewer (per viewer).

Also corrects the website path → route table, wrong since the module-system
cutover on 2026-08-12: core's NAV is eight rows, not sixteen, and the nine shard
rows moved to `/uo/*`. A nav override on any shard row was ignored, an added link
to one handed off to a browser, and the sort-key line was wrong. Two existing
tests had been passing vacuously since that day.

An inbox link to an event now opens the app rather than a Custom Tab, through
`resolveWebPath` rather than a second mechanism — so its "a query hands off" rule
gains exactly one exception, `run` on an event page.

The emulator walk found three defects that 563 green tests did not:

- the three player game-data rows read `/player/shard/*` and were not gated, so
  they rendered and 404'd; the test meant to catch that asked whether every row
  *with a feature* declared the capability, and those three have none. It now
  asks by route.
- `score` is DECIMAL(18,4) and was declared an integer, so one `318.5` made
  kotlinx refuse the entire body and a 200 rendered as a server error — latent on
  the public results table for every visitor.
- a drawer route's view model outlives a sign-out, so signing in as a second
  account showed it the first account's participation history with no request
  made at all.

570 tests, 0 failures.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016wDDVXWMDz82WqE1i969r4
2026-09-08 13:08:43 -05:00
2026-07-19 01:38:07 -05:00
2026-07-19 01:38:07 -05:00
2026-07-19 01:38:07 -05:00
2026-07-19 01:38:07 -05:00
2026-07-19 01:38:07 -05:00

Runic Gateway — Android app

A native Android client for a Runic Gateway shard's public site + player self-service. It is purely an API client of the website backend — it never talks to the link/ sidecar or the game shard directly, and it ships none of the shard/sidecar wiring. It surfaces the same content and player features as the website's browser client, minus every administrative/management console.

The authoritative design contract is docs/android/PLAN.md in the RunicGateway/docs repo. The authoritative API reference is the committed OpenAPI spec at website/server/swagger/swagger-output.json.

Status

M0 — repo scaffold. Gradle + Compose + Hilt skeleton with CI (lint + unit test + debug build). The functional Kotlin pass (M1M4) and the design pass (M5) follow — see the plan's milestones (§9).

Stack

Concern Choice
Language / UI Kotlin + Jetpack Compose (Material 3)
Navigation Navigation-Compose, single-activity
HTTP Retrofit + OkHttp, kotlinx.serialization
Async Coroutines + Flow
DI Hilt
Prefs / base URL Jetpack DataStore
Tokens at rest EncryptedSharedPreferences
Images Coil
Min SDK Android 10 (API 29)
Target / compile SDK 35

Dependency and plugin versions are pinned in gradle/libs.versions.toml.

Build

Requires JDK 17 and the Android SDK (ANDROID_HOME / local.properties).

./gradlew assembleDebug     # build a debug APK -> app/build/outputs/apk/debug/
./gradlew test              # JVM unit tests
./gradlew lint              # Android lint
./gradlew installDebug      # install on a connected device/emulator

The app self-configures its server URL on first run (PLAN.md §3), so a single build works against any shard's website — there is no compiled-in API host.

CI

.gitea/workflows/pr-checks.yml gates PRs into main and edge with ./gradlew lint test assembleDebug on the org's self-hosted runner (JDK 17 + Android SDK). Debug builds are auto-signed, so the gate needs no secrets. This pipeline is verified green end-to-end on the runner (M0). A signed release APK attached to a Gitea release comes at M6.

edge is in the trigger deliberately: a workstream that lands its phases on a working branch before one cutover PR into main otherwise gets no CI at all until the cutover — which is what happened to all nine M12 phase PRs (docs/website/ENGAGEMENT.md §7.1 Q8). sonarqube.yml is unaffected: it is a push-on-main analysis, not a PR gate.

The workflow carries a few runner-specific accommodations (each explained in comments in the file), because this self-hosted runner differs from a stock GitHub runner:

  • JDK 17 is installed via apt (not actions/setup-java) — the runner can't resolve api.adoptium.net, while the Ubuntu mirrors are reachable.
  • SDK packages are installed explicitly via sdkmanager, with set +o pipefail so yes dying of SIGPIPE doesn't fail the step.
  • gradlew is chmod +x'd in the run step — the runner's checkout does not preserve the git executable bit, so ./gradlew alone fails with "Permission denied".

Contributing

See CONTRIBUTING.md. AI-assisted contributions must be disclosed (org policy): tick the PR box naming the tool and add a Co-Authored-By trailer to AI-authored commits. Licensed GPL-3.0-or-later.

Description
No description provided
Readme 10 MiB
v0.5.0 Latest
2026-08-08 16:28:50 +00:00
Languages
Kotlin 99.9%
Python 0.1%