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:

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).