fix(shard): decode in-game serials as hex strings, not numbers #22

Merged
whitlocktech merged 1 commits from fix/shard-serial-decode into main 2026-07-22 01:16:56 +00:00
Member

Summary

The Guilds board (and Champs/Governors) crashed the app on open. Root cause: the public shard board DTOs typed in-game serials (and actor webId) as Long, but the wire protocol sends them as opaque hex strings ("0x1A2B") — see docs/link/INTEGRATION.md §1 and the C# emitters (BridgeJson.Ser/Actor). The website returns board payloads verbatim, so a guild leader / champ / governor carrying a hex serial threw JsonDecodingException out of the Retrofit converter and escaped the collecting coroutine → FATAL EXCEPTION: main.

Per the "API is the source of truth" rule, the DTOs are corrected to match the API.

Changes

  • DTOs → String (data/api/dto/ShardDto.kt): ActorDto.serial & webId, ChampDto.serial, HouseDto.serial, OnlineStaffDto.serial. Guild id stays Long — it's genuinely numeric on the wire (.Num).
  • Live-frame reads (ChampsViewModel, HousesViewModel): champ.remove / house.decay now read serial via stringField. Previously longField returned null on a hex serial, so every board removal and live IDOC update was silently dropped — two dormant bugs the type mismatch was masking.
  • Defense in depth (core/result/ApiResult.kt): safeApiCall now catches SerializationExceptionHttpError(502)ErrorKind.SERVER, so any future contract drift degrades to a retry-able error screen instead of crashing.
  • Tests: ShardDtoTest / ApiResultTest updated to the real hex-string wire shapes (previously encoded the wrong numeric assumption, which is why CI stayed green); added OnlineStaffDto + serialization-error cases.
  • Fixed a misleading comment in PlayerShardDto.kt that claimed the public boards use numeric serials (the origin of the mistake).

Testing

  • ./gradlew :app:testDebugUnitTest — green.
  • Manually verified on an emulator against a mock backend using the real wire shapes: Guilds, Champs, Governors, Houses all render with hex-serial/webId actors; a live champ.remove correctly removes a spawn and a live house.decay IDOC frame adds a house — both previously dropped.

AI disclosure

  • This PR was authored with AI assistance (Claude Code, Opus 4.8), per org policy.

🤖 Generated with Claude Code

https://claude.ai/code/session_01NgyHnrNa8WwG3doxvxjuCr

## Summary The Guilds board (and Champs/Governors) crashed the app on open. Root cause: the public shard board DTOs typed in-game **serials** (and actor `webId`) as `Long`, but the wire protocol sends them as **opaque hex strings** (`"0x1A2B"`) — see `docs/link/INTEGRATION.md` §1 and the C# emitters (`BridgeJson.Ser`/`Actor`). The website returns board payloads verbatim, so a guild leader / champ / governor carrying a hex serial threw `JsonDecodingException` out of the Retrofit converter and escaped the collecting coroutine → `FATAL EXCEPTION: main`. Per the "API is the source of truth" rule, the DTOs are corrected to match the API. ## Changes - **DTOs → `String`** (`data/api/dto/ShardDto.kt`): `ActorDto.serial` & `webId`, `ChampDto.serial`, `HouseDto.serial`, `OnlineStaffDto.serial`. Guild `id` stays `Long` — it's genuinely numeric on the wire (`.Num`). - **Live-frame reads** (`ChampsViewModel`, `HousesViewModel`): `champ.remove` / `house.decay` now read `serial` via `stringField`. Previously `longField` returned `null` on a hex serial, so **every board removal and live IDOC update was silently dropped** — two dormant bugs the type mismatch was masking. - **Defense in depth** (`core/result/ApiResult.kt`): `safeApiCall` now catches `SerializationException` → `HttpError(502)` → `ErrorKind.SERVER`, so any future contract drift degrades to a retry-able error screen instead of crashing. - **Tests**: `ShardDtoTest` / `ApiResultTest` updated to the real hex-string wire shapes (previously encoded the wrong numeric assumption, which is why CI stayed green); added `OnlineStaffDto` + serialization-error cases. - Fixed a misleading comment in `PlayerShardDto.kt` that claimed the public boards use numeric serials (the origin of the mistake). ## Testing - `./gradlew :app:testDebugUnitTest` — green. - Manually verified on an emulator against a mock backend using the real wire shapes: **Guilds, Champs, Governors, Houses** all render with hex-serial/`webId` actors; a live `champ.remove` correctly removes a spawn and a live `house.decay` IDOC frame adds a house — both previously dropped. ## AI disclosure - [x] This PR was authored with AI assistance (Claude Code, Opus 4.8), per org policy. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01NgyHnrNa8WwG3doxvxjuCr
wtclaude added 1 commit 2026-07-22 01:09:39 +00:00
fix(shard): decode in-game serials as hex strings, not numbers
All checks were successful
PR Checks / android-build (pull_request) Successful in 5m44s
1a14d47d5c
The public shard board DTOs typed in-game serials (and actor webId) as
Long, but the wire protocol (docs/link/INTEGRATION.md §1) sends them as
opaque hex strings ("0x1A2B"). The website returns board payloads
verbatim, so a guild leader / champ / governor carrying a hex serial
threw JsonDecodingException out of the Retrofit converter and crashed the
app on the Guilds/Champs/Governors boards. The API is the source of
truth, so the DTOs are corrected to match it.

- ActorDto.serial/webId, ChampDto.serial, HouseDto.serial,
  OnlineStaffDto.serial: Long -> String
- champ.remove / house.decay live frames now read serial via stringField;
  longField returned null on a hex serial, silently dropping every board
  removal and live IDOC update
- safeApiCall now catches SerializationException -> ErrorKind.SERVER, so
  any future contract drift degrades to a retry-able error instead of a
  crash (defense in depth)
- DTO + result tests updated to the real hex-string wire shapes

AI-assisted: authored with Claude Code (Opus 4.8).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NgyHnrNa8WwG3doxvxjuCr
whitlocktech approved these changes 2026-07-22 01:10:44 +00:00
whitlocktech scheduled this pull request to auto merge when all checks succeed 2026-07-22 01:10:57 +00:00
whitlocktech merged commit befbc01670 into main 2026-07-22 01:16:56 +00:00
whitlocktech deleted branch fix/shard-serial-decode 2026-07-22 01:16:57 +00:00
Sign in to join this conversation.
No description provided.