Transmission #067: Server CI sparse checkout includes AsyncAPI spec
Server CI uses a sparse Git checkout (Unity tree omitted) and already pulled in repo-root openapi.yaml for Gradle evaluation. The WebSocket event loop added the same pattern for asyncapi.yaml, but the workflow never listed that file — so ./gradlew test failed on PRs with “AsyncAPI spec missing” even though the spec is committed at the repo root.
server-ci.yml and release.yml now include asyncapi.yaml in sparse checkout and path filters, mirroring OpenAPI. Release’s server-change detection treats AsyncAPI edits as server changes so post-merge shadowJar builds still see the contract.
asyncApiGenerateServer on Gradle 9
CI then failed on :asyncApiGenerateServer: a doFirst block called nested exec {} on an Exec task, which Gradle 9 no longer allows. Fresh runners also lack server/asyncapi-generator/node_modules.
Fix: register asyncApiGeneratorNpmInstall as its own Exec task (runs npm install only when js-yaml is missing) and make asyncApiGenerateServer depend on it. Merged main into events_api for the selection-tags work landed on main.
syncAsyncApiGeneratedServer must not wipe models
:compileJava failed with missing com.helerion.events.model even though model POJOs are committed under events/model/. Gradle Sync into src/generated/java/com/helerion/events/ deleted sibling model/ because server codegen only outputs api/ and ws/. CI task order often ran server sync after model sync, so models vanished before compile.
Fix: split into syncAsyncApiGeneratedServerApi and syncAsyncApiGeneratedServerWs, each syncing only into its subdirectory so events/model/ is preserved.
WebSocket integration test client visibility (Java 25)
CharacterEventsWsIntegrationTest timed out waiting for inbound frames. Tyrus reflectively invokes @ClientEndpoint lifecycle methods; a private nested CollectingClient throws IllegalAccessException on JDK 25, so @OnMessage never ran even though the server replied (verified with wscat).
Fix: make the client class public, wait on @OnOpen before sending, and annotate @OnClose.
PDD — server-authoritative character sync (detailed design)
Prompt-Driven Development for multiplayer overworld sync (extend PR #75 WebSockets) finished requirements Q1–Q15 and landed design/detailed-design.md under .agents/planning/2026-06-24-server-authoritative-characters/.
The design specifies dual WebSocket channels (/events achievements unchanged, new /world at 30 Hz), WGS84 lat/lon on the wire for true GPS multiplayer, Protomaps-backed server spawn/AI (port of GoblinRoadSpawner / GoblinAI), ~250 m interest, PvP with server combat arbitration, full PostgreSQL persistence with batched checkpoints, and blocked interiors while world sync is active. Ship bar: two physical phones; CI: Gradle integrationTest multi-client WS scenario.
PDD — implementation plan (15 steps)
implementation/plan.md breaks delivery into three mergeable PRs: (1) AsyncAPI world channel + two-player visibility integration test, (2) Protomaps spawn, 30 Hz mob AI, PvP, persistence + full server CI, (3) Unity HelerionWorldSession, remote proxies, combat bridge, and client guards for the two-phone demo.
Server-authoritative world sync (implementation)
Land v1 overworld multiplayer on the existing Tyrus stack:
asyncapi.yaml—characterWorldchannel schemas (reportPresence,worldDelta,combatIntent, etc.)- Flyway V5 —
world_entity+world_region_config; batched DB checkpoints (not per-tick writes) com.helerion.world.*— 30 HzWorldSimulationService, 250 m interest, server mob spawn/AI, PvP combat, second WS/v1/characters/{id}/world- Unity —
HelerionWorldSession,HelerionWorldCoordinator,RemoteEntityRegistry; local goblin spawn and interior entry blocked while world sync is active; melee on remote proxies sendscombatIntent - CI —
CharacterWorldWsIntegrationTest(two simulated clients + shared mob kill)
Scene wiring still needed: assign remote player/mob prefabs on RemoteEntityRegistry and add HelerionWorldCoordinator next to HelerionEventSession. Protomaps tile fetch on the server is stubbed via radial spawn for v1 (env PROTOMAPS_API_KEY reserved for follow-up).