Devstack Runtime Coverage & Real-Client Verification Plan¶
Status: approved 2026-09-15. Owner: enterprise-readiness criterion #1 (see
ENTERPRISE_READINESS.md).
0. Why this document exists¶
Chasing pure-unit 90/80 on rtp-core has hit diminishing returns. The remaining
~26.6k missed instructions are concentrated in code that a plain-JVM harness
cannot reach honestly: the RTP constructor's server-bound bootstrap lambdas,
platform-adapter seams, and the full teleport/dispatch pipelines that only exist
at runtime. The devstack already exercises exactly that code with a JaCoCo
-javaagent attached to every backend/lobby, so runtime-attested coverage is the
more honest way to close the last mile.
This plan splits the work into two independent tracks:
- Track A - Intermediate release (product/versioning). Ship today's strong state; lock in floors; re-scope criterion #1's definition of done.
- Track B - Devstack + real-client runtime verification (evidence/coverage). Close the remaining criterion #1 gap with runtime proof instead of increasingly artificial unit tests.
Do Track A first (cheap, unblocks shipping). Then run Track B as the ongoing last mile.
What already exists (this is a wiring/plan job, not a build job)¶
devstack/docker-compose.coverage.ymlattaches-javaagent:jacocoagent.jar=...,includes=io.github.dailystruggle.rtp.*,dumponexit=trueto all 3 backends + 2 lobbies, writing./jacoco/<service>.exec.devstack/run-acceptance.ps1 -Coveragelayers that overlay and runs:jacocoServerReportat the end (registered in rootbuild.gradle, mergesdevstack/jacoco/*.execintobuild/reports/jacoco/server/).- The acceptance harness drives
boot,heartbeat,killmidflight,killswitch, andrtptestheadlessly (Redis introspection +rcon/console). Onlyroundtripneeds a live client - the pipeline is gated onPlayerJoinEvent, which cannot be synthesized headlessly. /rtp test accessor//rtp test fullforce a mid-session.execdump, so a client session's coverage is captured without stopping the stack.
1. Track A - intermediate release prompt sequence¶
Run these prompts one per session (or in order within a session). Each is written to be self-contained and to end with a concrete, verifiable artifact.
Prompt A1 - re-scope criterion #1 in the readiness doc¶
In
docs/dev/ENTERPRISE_READINESS.md, re-scope criterion #1's definition of done: strict 90/80 unit JaCoCo floors remain the bar for the pure-logic modules (already met by 7/9), while the platform/pipeline seams inrtp-core(theRTPbootstrap lambdas, teleport/dispatch pipeline, adapter glue) are credited by runtime-attested devstack coverage instead. Keep criterion #1 marked PARTIAL, add a short subsection pointing atdocs/dev/DEVSTACK_COVERAGE_PLAN.md, and state that the intermediate release is cut at the current measured state. Docs-only change; verify with a mojibake scan of the diff.
Prompt A2 - ratchet the rtp-core floors to measured values¶
Run
:rtp-core:test :rtp-core:jacocoTestReport -Pcoverage, read the fresh module + per-package numbers frombuild/reports/jacoco/test/jacocoTestReport.xml, then usescripts/ratchet-coverage.py(monotonic-upward only) to raisecoverageFloors[':rtp-core']in rootbuild.gradlefrom the current0.69/0.54toward the measured~0.816/0.658(leave the documented ~1-2 pt stale-exec margin; never lower a floor). Keep the safety-package rules at or above the new module floor. Verify with:rtp-core:jacocoTestCoverageVerification -Pcoveragegreen, then update the floor comment inbuild.gradlewith the new measured baseline.
Prompt A3 - do the same for rtp-proxy-common¶
Repeat the ratchet for
:rtp-proxy:rtp-proxy-common: measure viajacocoTestReport -Pcoverage, raise its0.78/0.65floor toward the measured value withscripts/ratchet-coverage.py, and confirmjacocoTestCoverageVerification -Pcoveragestays green.
Prompt A4 - cut the intermediate release¶
Prepare the intermediate release: update
CHANGELOG.md(diff against the last released tag, tag Pro-exclusive lines with**(Pro)**, absolute phrasing), bump the version per the project convention, and confirm the CI gates (build shadowJar -Pcoverage -PstaticAnalysis,diff-coverage.py,dependency-check) are green. Do NOT commit or tag unless I explicitly ask. Report the release-ready state and any remaining blockers.Note:
:rtp-core:spotbugsMaincurrently carries a pre-existing 841-finding backlog onsrc/main(unrelated to test-only work). If a fullbuildis required for the release gate, treat that backlog as a separate, tracked item - do not attempt to clear it inside the release-cut prompt.
2. Track B - devstack real-client runtime test plan¶
Goal: produce a merged runtime JaCoCo report that credits the platform/pipeline
seams, and a repeatable evidence trail, then diff it against the unit report to
show exactly which rtp-core classes the devstack uniquely covers.
2.1 Preconditions (one-time per machine)¶
- Docker Desktop running (Docker Engine 29+, min API 1.40 - same daemon the Testcontainers SQL tier now uses).
- Build + stage jar from repo root:
.\gradlew.bat :rtp-plugin:remapJar Copy-Item rtp-plugin\build\libs\LeafRTP-Pro-*.jar devstack\jars\plugin\ - Provision secrets (once):
cd devstack Copy-Item .env.example .env # set RTP_NET_SECRET to a 32-byte base64 string Copy-Item shared\forwarding.secret.example shared\forwarding.secret - Ensure the JaCoCo agent jar is present in
devstack/jacoco/(the overlay mounts./jacocoand referencesjacocoagent.jar). Stage it from the JaCoCo toolchain if missing.
2.2 Headless coverage sweep (no client needed)¶
Captures dispatch, heartbeat, reservation reap, kill-switch, and per-service
/rtp test accessor with the agent attached:
cd devstack
.\run-acceptance.ps1 -Coverage -Scenario boot
.\run-acceptance.ps1 -Coverage -Scenario heartbeat
.\run-acceptance.ps1 -Coverage -Scenario rtptest
.\run-acceptance.ps1 -Coverage -Scenario killmidflight
.\run-acceptance.ps1 -Coverage -Scenario killswitch
Expected verdicts (from the README headless-evidence table):
| Scenario | Pass condition |
|---|---|
| boot | docker compose ps reports all eight services Up |
| heartbeat | redis-cli lists 5 backend + 2 proxy keys |
| rtptest | [RTP test/accessor] pass=true per backend + lobby |
| killmidflight | reservation row clears within reservation.ttlMs + reapInterval |
| killswitch | Lua claim returns KILL_SWITCH; proxy log line asserted |
2.3 Real-client roundtrip (the one manual step)¶
The cross-server /rtp pipeline is gated on PlayerJoinEvent; a live 1.21.x
Minecraft client is required. Run once on Pro, once on Lite.
Pro path:
1. .\run-acceptance.ps1 -Coverage -Scenario roundtrip (boots the stack with the
agent attached and waits for the manual step).
2. Connect a 1.21.x client to localhost:25577 (proxy-a). You land on lobby-a.
3. /server backend-a, then /rtp. Confirm the teleport in-client and confirm
the destination backend log shows the local teleport-pipeline arrival line.
4. /server backend-b and /server backend-c, run /rtp on each (exercises both
scheduler families - Paper on a, Folia on b/c).
5. Before disconnecting, on each backend run /rtp test full to force an .exec
dump (belt-and-suspenders alongside dumponexit).
6. Disconnect.
Lite path (DB-free plugin-message transport):
1. .\run-acceptance.ps1 -Coverage -Lite -Scenario roundtrip.
2. Connect to :25577, then /server backend-b and /server backend-c once each
to seed availability gossip (a backend only emits heartbeat/auto-detects once a
player connection exists on it).
3. /rtp and confirm the cross-server move + destination-log arrival line.
Evidence here is visual + log-based (reservation tokens are empty by design on
lite).
2.4 Merge, report, and diff¶
# run-acceptance.ps1 -Coverage already calls this at the end; run manually if needed:
.\gradlew.bat :jacocoServerReport
- HTML/XML land in
build/reports/jacoco/server/. - Parse
jacocoServerReport.xmlfor module +common.tasks.teleport,common.selection.region, rootRTP, andnetwork/dispatch packages. - Diff the runtime report against the unit report
(
rtp-core/build/reports/jacoco/test/jacocoTestReport.xml) to identify classes covered only at runtime. That delta is the evidence that closes the honest part of criterion #1.
2.5 Acceptance criteria & evidence artifacts¶
- All §2.2 headless scenarios pass with the verdicts above.
- Both §2.3 roundtrips (Pro + Lite) succeed: in-client teleport + destination-log arrival line.
jacocoServerReportshows theRTPbootstrap/pipeline/adapter classes covered at runtime that the unit report misses (record the class list + before/after numbers).- Evidence retained:
devstack/acceptance-evidence.log,docker compose psoutput, the mergedjacocoServerReportHTML, and the runtime-vs-unit diff. - Cite the merged numbers in
ENTERPRISE_READINESS.mdsection 2 and flip the runtime-attested portion of criterion #1 accordingly.
3. Positioning & trade-offs (keep honest)¶
- Devstack coverage is not deterministically per-PR gateable (needs Docker +
one manual client hop for
roundtrip). Treat it as scheduled/nightly + release-gate evidence, mirroring how-PfullTestsis already handled - not a per-PR blocker. - Unit JaCoCo floors stay the fast per-PR gate; the devstack report is the release-time attestation for the runtime-only seams.
- Do not pretend the god-class bootstrap is unit-coverable: credit it via the runtime report, and say so in the scorecard.
4. See also¶
devstack/README.md- topology, coverage overlay, lite/lobby-world overlays, acceptance harness.docs/admin/proxies/CROSS_SERVER_VERIFICATION.md- operator-facing manual verification for the same scenarios.
docs/dev/ENTERPRISE_READINESS.md- scorecard and criterion #1.docs/dev/MULTI_SERVER_PLAN.md- phase status.