Land AForce2 in-game dialogs PR (three dialog styles from the design mocks)
Written 2026-10-14 20:20 UTC. RE-VERIFY: all state below is a write-time snapshot. Re-check with
gh pr view 25 --repo CodexCoder21Organization/AForce2 --json state,mergeStateStatus,statusCheckRollup,mergedAt,headRefOid
and gh api repos/CodexCoder21Organization/AForce2/commits/<head>/check-runs.
Mission summary
The user posted three dialog UI mock-ups and asked: "Add these three images to the https://github.com/CodexCoder21Organization/AForce2 repository in a folder for design mocks or something. Commit/merge those changes. While we're waiting for that to merge, in parallel, we can start implementing the dialogs. Each screenshot demonstrates a different dialog UI design. We want to implement all three different dialog UI designs, fully implement them, so we can optionally use them in the games."
- Mock images: DONE, merged in https://github.com/CodexCoder21Organization/AForce2/pull/24 (
docs/design-mocks/dialogs/).
- Dialog implementation: code complete, reviewed CLEAN (5 adversarial review rounds), awaiting CI + merge queue in https://github.com/CodexCoder21Organization/AForce2/pull/25.
Remaining work: get kotlin.build (remote) green on the PR head and land the PR through the (squash) merge queue. No production deploy was requested.
State at write time
- PR https://github.com/CodexCoder21Organization/AForce2/pull/25: OPEN, head
fe04816022139691daa83c82cfbfea914cfdc591 on branch in-game-dialogs, rebased onto main after https://github.com/CodexCoder21Organization/AForce2/pull/22 merged. mergeStateStatus BLOCKED (only because required CI was still running).
- Checks on
fe04816: kotlin.build (remote) (required) in_progress — https://buildtest.kotlin.build/run?id=9af81487 ; kotlin.build (kompile-remote-build) (informational) in_progress.
- Not enqueued yet. A build-watchman
--to-merged watcher was running and was deliberately stopped at handoff time (session ending); nothing is watching the PR now.
- Local verification: full suite 207/207 passed at
fe04816 against the exact jar later published as aforce2-fat:0.0.25 (hash verified).
What was built (on in-game-dialogs)
src/com/aforce2/dialogs/GameDialog.kt — foundation: DialogActionKind {PRIMARY, SECONDARY, DANGER}, DialogAction, abstract GameDialog (object API, Show() composable, keyboard: arrows/Tab move selection, Enter/NumPadEnter/Space press, Escape dismiss, acting on key-up with held-key protection via a process-wide HeldKeyRecord; hover-after-real-movement + click), GameDialogs singleton (show, dismiss, shown, isShowing, keyDown/keyUp/isKeyHeld/releaseAllKeys), GameDialogLayer with a 60% black scrim.
- Three styles from the mocks:
SystemNoticeDialog.kt (min window 660x420), ConnectionAlertDialog.kt (signal bars, min 480x400), SecureAuthenticationDialog.kt (blinking cursor, min 400x300), plus PlainDialog.kt. Max 3 actions each. Animation is driven by the Compose frame clock (deterministic goldens).
OffscreenDialogScene.kt + OffscreenRenderer.renderDialog(...) for headless tests (uses an internal Compose KeyEvent factory with an OptIn, documented).
- Wiring:
src/Display.kt and multiplayer/client/MultiplayerDisplay.kt record key press/release into GameDialogs, release all keys on window focus loss, and wrap screens in GameDialogLayer; UserIO and multiplayer keyboard controls are gated while a dialog shows; AForce2.cleanup() dismisses any dialog.
- Fonts (OFL, with licence files + NOTICE.txt): VT323, Share Tech Mono, UnifrakturCook Bold.
- 50+ dialog tests in
tests/ (goldens screenshot*Dialog*, fit, scroll, label-wrap, keyboard, mouse, held-key, focus-loss, stray key-up, multiplayer gating). README has a Dialogs section; docs/headless-mode.md updated.
Published artifacts (not on main yet)
aforce2:aforce2-fat:0.0.25 published to https://kotlin.directory from fe04816 (the PR head). All tests/*.kts on the branch pin it. Becomes consistent with main once the PR merges.
aforce2:aforce2-fat:0.0.24 was also published from the branch before the rebase onto 0.0.19–0.0.23; it lacks those changes and is superseded — do not pin it (noted in build.kts changelog).
- Nothing deployed anywhere.
Relevant PRs / refs
| Repo |
Branch |
Remote head SHA |
PR |
What is on it |
State |
| CodexCoder21Organization/AForce2 |
in-game-dialogs |
fe04816022139691daa83c82cfbfea914cfdc591 |
https://github.com/CodexCoder21Organization/AForce2/pull/25 |
complete dialogs feature |
builds, 207/207 local; CI running; the one to land |
| CodexCoder21Organization/AForce2 |
design-mocks/dialogs |
ed44b1bd7409f8db6138e75194fbcd709fd198da |
https://github.com/CodexCoder21Organization/AForce2/pull/24 |
mock images |
merged; safe to delete |
| CodexCoder21Organization/AForce2 |
dialogs/foundation, dialogs/styles, dialogs/fit, dialogs/round2, dialogs/round3, dialogs/system-notice, dialogs/connection-alert, dialogs/secure-auth |
(fa16e18, 0d176d6, 76b9a49, 1d5de6e, ea1bbab, 8d1e034, 5a8e1d6, e7d319d) |
no PR |
intermediate iteration branches |
all superseded by in-game-dialogs; safe to delete |
All local checkouts were swept (no uncommitted work, stashes, or unpushed commits) and then deleted.
Next steps
- Re-verify the PR state and the head's checks (commands above).
- Watch and land it:
~/bin/coursier launch buildwatchman:build-watchman:0.0.21 -r https://kotlin.directory -- --repo CodexCoder21Organization/AForce2 --pr 25 --to-merged as a tracked background task; branch on exit code (0 merged, 1 red, 4 evicted, 5 idle-timeout).
- If
kotlin.build (remote) goes red: read the buildtest run log, reproduce the failing test locally by file path, fix the cause (never bypass, never weaken). If main moved, rebase; if source changed, publish a new aforce2-fat version (next free number — check kotlin.directory, other branches have claimed numbers before) and bump every tests/*.kts pin and build.kts coordinates.
- After merge, optionally delete the superseded
dialogs/* and design-mocks/dialogs branches.
Reusable knowledge
- Build:
scripts/build.bash; tests: scripts/test.bash --local --test tests/<file>.kts. Select tests by file path — selecting by name compiles every test and OOMs the box. Serialize heavy builds/tests on one machine.
- Tests depend on a published fat jar (
@file:WithArtifact("aforce2:aforce2-fat:<ver>")). For local iteration, install a locally built fat jar under ~/.cache/coursier/v1/bldbinary/repository/aforce2/aforce2-fat/<ver>-local-x/ and delete it afterwards (stale bldbinary entries mask changes).
- Test
.kts scripts are not compiled with the Compose plugin: no @Composable code in tests — hence the object-based GameDialog API and OffscreenDialogScene.
- Golden images: a missing golden is recorded and the test fails once; commit the recorded file.
- Publishing:
~/bin/coursier launch community.kotlin.maven.artifact.publishing:community-kotlin-maven-artifact-publishing:0.0.6 -r https://kotlin.directory -- --artifact-dir <dir> --api-url https://api.kotlin.directory/upload --force (POM copied from a previous version with the version swapped).
gh pr edit fails on this repo (Projects classic GraphQL error); edit PR bodies with gh api -X PATCH repos/CodexCoder21Organization/AForce2/pulls/25 -F body=@file.
git push --force-with-lease=<branch>:<sha> needs the full SHA.
- Known accepted gaps (stated in the PR): the window focus-loss listener cannot be tested headlessly; a key still physically held when focus returns acts as a fresh press (documented in KDoc). System Notice and Connection Alert need minimum window sizes larger than the current game windows (constants
*_MIN_WINDOW_*).