17 KiB
Beacon Session Safety Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Make beacon activation session-only, enforce a five-minute minimum, disable the correct category after an external QRG move beyond 5 kHz, and render a reason-aware traffic-colour MyBeacon chip.
Architecture: A controller-owned BeaconSessionState coordinates the existing observable runtime flags, category references, inactive reasons and send failures. Manual QRG commits and UCXLog/Win-Test reports enter through distinct controller methods; a structured model snapshot crosses the existing thread-status boundary and app-desktop localises and styles it.
Tech Stack: Java 21, Kotlin 2.2, Compose Multiplatform 1.8.x, JUnit 5, Mockito, Gradle wrapper
Spec: docs/superpowers/specs/2026-11-11-beacon-session-safety-design.md
Global Constraints
- Work directly on
main, as explicitly authorised. - Do not stage, commit, push, merge, tag, release or deploy; commit steps are deliberately omitted because Marc authorised none of them.
- Keep source comments and Javadoc in English; user-facing DE/EN text follows the project writing style.
- Use exactly 5 minutes and 5,000 Hz; exactly 5,000 Hz must not disable a beacon.
- Do not add a production dependency or change ON4KST/UCXLog/Win-Test framing.
- Keep the shared timer, initial ten-second check, outbound queue and second-chat eligibility unchanged.
- Do not execute UI callbacks while holding an internal beacon-state lock.
- Preserve unrelated local changes and inspect the diff before and after every task.
Review Focus
- A secondary beacon using
MYQRGmust be refused or disabled when the primary QRG is invalid, even though its own reference remains the secondary QRG; Task 1 pins this cross-template case. - An external report with no reference must establish the reference without disabling; Task 1 pins this first-report case.
- Without SO2R a Win-Test/UCXLog report must affect only the main beacon; Task 3 pins both integrations.
- An old XML file with enabled flags and a one-minute interval must load as disabled with a five-minute interval and later write
false; Task 2 pins the migration. FAILEDmust stay red without an orange flash while ordinary worker chips retain current theme colours; Task 4 pins both sides.
Task 1: Pure beacon session policy and structured status
Files:
- Create:
core/src/main/java/kst4contest/controller/BeaconFrequencyParser.java - Create:
core/src/main/java/kst4contest/controller/BeaconSessionState.java - Create:
core/src/main/java/kst4contest/model/BeaconStatusSnapshot.java - Create:
core/src/test/java/kst4contest/controller/BeaconFrequencyParserTest.java - Create:
core/src/test/java/kst4contest/controller/BeaconSessionStateTest.java
Interfaces:
-
Produces:
BeaconFrequencyParser.parseHz(String): OptionalLong. -
Produces:
BeaconSessionState.Channel { MAIN, SECOND }and synchronized policy methodsresetSession(ChatPreferences),setEnabled(Channel, boolean, ChatPreferences),onManualQrgChanged(Channel, String, ChatPreferences),onExternalQrgChanged(Channel, long, ChatPreferences),onTemplateChanged(Channel, ChatPreferences),recordSendFailure(Channel, boolean)andsnapshot(ChatPreferences): BeaconStatusSnapshot. -
Produces: explicit
BeaconStatusSnapshotDTO with aggregate stateOFF,PARTIAL,ON,FAILED, plus main/second category DTOs carrying availability, enablement, failure andInactiveReason. -
Step 1: Write parser tests for
144.300,144,300,144.300.03,1296.123.000, compact144300, compact10368100, blanks, malformed text, non-positive values and overflow. -
Step 2: Run the parser test and verify RED.
Run:
.\gradlew.bat :core:test --tests kst4contest.controller.BeaconFrequencyParserTest --console=plainExpected: compilation failure because
BeaconFrequencyParserdoes not exist. -
Step 3: Implement
BeaconFrequencyParserwith integer arithmetic and the QRG forms already used by the client; accept only a complete positive frequency that maps to a supportedBand. -
Step 4: Re-run the parser test and verify GREEN.
-
Step 5: Write session-policy tests for initial reasons, independent enablement, placeholder requirements in either template, partial aggregate activation, manual reference creation/rebase, invalid manual clearing, cross-template invalidation, first external reference, 5,000/5,001-Hz boundaries, per-category external changes, reason retention and send-failure clearing.
-
Step 6: Run
BeaconSessionStateTestand verify RED because the policy and DTO are absent. -
Step 7: Implement the DTO and policy so the existing observable preference booleans are projection only and no listener/callback leaves a synchronized method.
-
Step 8: Re-run both focused tests and verify GREEN.
-
Step 9: Inspect the task diff for duplicated state, floating-point frequency comparisons or display strings in
core.
Task 2: Session-only persistence and five-minute migration
Files:
- Modify:
core/src/main/java/kst4contest/model/ChatPreferences.java - Modify:
core/src/main/java/kst4contest/controller/ChatController.java - Modify:
core/src/main/resources/praktiKSTpreferences.xml - Modify:
core/src/test/java/kst4contest/model/ChatPreferencesBeaconStateTest.java - Modify:
core/src/test/java/kst4contest/model/ChatPreferencesRoundTripTest.java - Modify:
app-desktop/src/test/kotlin/kst4contest/view/compose/tabs/BeaconTabStateTest.kt - Modify:
app-desktop/src/test/kotlin/kst4contest/view/i18n/RemainingTextsFollowTheLanguageTest.kt
Interfaces:
-
Consumes: Task 1
BeaconSessionState. -
Produces:
ChatController.MIN_BEACON_INTERVAL_MINUTES == 5and session reset during controller/profile construction without reset on reconnect. -
Step 1: Replace persistence expectations with failing tests proving defaults are off, stored
trueis ignored, every full write stores both legacy/current enable tags asfalse, layout-only writes do not revive them, and intervals below five normalise in memory. -
Step 2: Add interval tests proving four is refused, five accepted, and an old stored one becomes five without changing a larger value.
-
Step 3: Run the focused core and desktop tests and verify RED.
Run:
.\gradlew.bat :core:test --tests kst4contest.model.ChatPreferencesBeaconStateTest --tests kst4contest.model.ChatPreferencesRoundTripTest :app-desktop:test --tests kst4contest.view.compose.tabs.BeaconTabStateTest --tests kst4contest.view.i18n.RemainingTextsFollowTheLanguageTest --console=plain -
Step 4: Make enable flags session-only: initialise both false, ignore XML enable values, always serialise
false, retain existing element names, and reset the policy only when a new controller/profile preferences object is installed. -
Step 5: Set and enforce the five-minute minimum in controller, XML defaults and preference-load normalisation; retain timer start/restart semantics.
-
Step 6: Re-run the focused tests and inspect their XML summaries.
-
Step 7: Inspect the task diff to confirm no reconnect path resets the session and no default was silently raised to ten minutes.
Task 3: Explicit manual and external QRG paths
Files:
- Modify:
core/src/main/java/kst4contest/controller/ChatController.java - Modify:
core/src/main/java/kst4contest/controller/ReadUDPbyUCXMessageThread.java - Modify:
core/src/main/java/kst4contest/controller/ReadUDPByWintestThread.java - Modify:
app-desktop/src/main/kotlin/kst4contest/view/compose/ChatInputState.kt - Modify:
app-desktop/src/main/kotlin/kst4contest/view/compose/ChatInputRow.kt - Modify:
app-desktop/src/main/java/kst4contest/view/ComposeChatInputActions.java - Modify:
app-desktop/src/main/kotlin/kst4contest/view/compose/tabs/BeaconTabState.kt - Modify:
app-desktop/src/main/kotlin/kst4contest/view/compose/SettingsTabs.kt - Modify: focused fake
ChatInputActionsimplementations in affected tests - Modify:
core/src/test/java/kst4contest/controller/BeaconControlTest.java - Modify:
core/src/test/java/kst4contest/controller/ReadUDPbyUCXMessageThreadLoopbackTest.java - Modify:
core/src/test/java/kst4contest/controller/WinTestStatusSlotTest.java - Modify:
app-desktop/src/test/kotlin/kst4contest/view/compose/ChatInputStateTest.kt - Modify:
app-desktop/src/test/kotlin/kst4contest/view/compose/tabs/BeaconTabStateTest.kt
Interfaces:
-
Produces: controller methods
applyManualQrg(RadioSlot, String),applyExternalQrg(RadioSlot, String, long),onMainBeaconTemplateApplied()andonSecondBeaconTemplateApplied(). -
Produces: a
ChatInputActions/ChatInputStatemanual-QRG commit method used byChatInputRowinstead of writing preferences directly. -
Consumes: Task 1 policy and Task 2 session lifecycle.
-
Step 1: Add failing controller tests for individual activation refusal, valid partial chip activation, user disable reason, manual rebase, invalid manual cross-template disabling and template-apply revalidation.
-
Step 2: Add failing chat-input tests proving both manual fields call the explicit action with the correct
RadioSlot/category and never write preferences directly. -
Step 3: Add failing UCXLog tests for exact Hz conversion, first-reference behaviour, >5-kHz main-only shutdown without SO2R and second-only shutdown for RadioNr 2 with SO2R.
-
Step 4: Add failing Win-Test tests for main/pass frequency selection, 100-Hz units, non-SO2R main-only and SO2R slot routing.
-
Step 5: Run the four focused test groups and verify RED.
-
Step 6: Implement the controller entry points so the preference value and policy update occur together on the existing UI-dispatch boundary and publish a fresh beacon status only after state is consistent.
-
Step 7: Route manual Compose commits through
ComposeChatInputActionswhile preserving focus/commit behaviour and existing displayedObservedValuemirrors. -
Step 8: Give both template draft properties a shared post-apply effect key that revalidates active beacons after all writes, without changing their validation-before-stage behaviour.
-
Step 9: Route UCXLog and Win-Test through
applyExternalQrgonly when their current QRG-sync setting already permits the update; preserve packet parsing and diagnostics. -
Step 10: Re-run focused tests and inspect XML summaries.
-
Step 11: Run related integration tests for SO2R routing, settings apply and chat-input rendering.
-
Step 12: Inspect the task diff for protocol/framing changes, toolkit calls in
core, or source inference through listeners.
Task 4: Beacon task status, translated tooltips and traffic colours
Files:
- Modify:
core/src/main/java/kst4contest/controller/BeaconTask.java - Modify:
core/src/main/java/kst4contest/controller/BeaconStatus.java - Modify:
core/src/main/java/kst4contest/model/ThreadStateMessage.java - Create:
app-desktop/src/main/kotlin/kst4contest/view/compose/BeaconStatusPresentation.kt - Modify:
app-desktop/src/main/kotlin/kst4contest/view/compose/StatusIndicatorState.kt - Modify:
app-desktop/src/main/kotlin/kst4contest/view/compose/StatusBar.kt - Modify:
app-desktop/src/main/i18n/strings_de.properties - Modify:
app-desktop/src/main/i18n/strings_en.properties - Modify:
core/src/test/java/kst4contest/controller/BeaconTaskTest.java - Modify:
core/src/test/java/kst4contest/controller/BeaconControlTest.java - Create:
app-desktop/src/test/kotlin/kst4contest/view/compose/BeaconStatusPresentationTest.kt - Modify:
app-desktop/src/test/kotlin/kst4contest/view/compose/StatusIndicatorStateTest.kt - Modify:
app-desktop/src/test/kotlin/kst4contest/view/compose/StatusBarScreenTest.kt - Modify: relevant i18n boundary tests
Interfaces:
-
Consumes: Task 1
BeaconStatusSnapshotand policy send-failure methods. -
Produces: optional structured beacon snapshot on
ThreadStateMessage; other thread messages remain unchanged. -
Produces:
BeaconStatusPresentationmapping snapshot plus current strings to label, tooltip and visual style. -
Step 1: Add failing core tests proving a failed active category yields
FAILED, a later successful run clears only its failure, disabled categories are not failed, and ordinary timer runs preserve inactive reasons. -
Step 2: Add failing presentation tests for bilingual per-category tooltips, initial/user/QRG/QRG-invalid reasons,
1/2, and no concrete frequency in the QRG reason. -
Step 3: Add failing colour-model tests for the exact light/dark OFF colours, ON/PARTIAL green, 200-ms orange highlight, FAILED red/white with highlight suppression and unchanged default-worker colours.
-
Step 4: Run focused core and desktop tests and verify RED.
-
Step 5: Attach the structured snapshot to the existing status message without adding translated display prose to
core. -
Step 6: Make
BeaconTaskrecord per-category outcomes while retaining text resolution, category validation and regular TX queue insertion. -
Step 7: Implement translated presentation and visual style only for messages carrying a beacon snapshot; keep action/provider ordering and stale-update protection intact.
-
Step 8: Implement the selected colours in
ThreadStatusButtonViewand suppress the highlight when style is FAILED. -
Step 9: Add aligned DE/EN i18n keys and re-run translation-boundary tests.
-
Step 10: Re-run focused tests and inspect XML summaries.
-
Step 11: Run the affected Compose screen tests and inspect a rendered light/dark result if the test harness emits one.
-
Step 12: Inspect the task diff to ensure no non-beacon chip acquired special colours or click behaviour.
Task 5: Targeted documentation, context and full verification
Files:
- Modify:
github_docs/de-Konfiguration.md - Modify:
github_docs/en-Configuration.md - Modify:
github_docs/de-Contest-Workflow.md - Modify:
github_docs/en-Contest-Workflow.md - Modify:
github_docs/de-Makros-und-Variablen.md - Modify:
github_docs/en-Macros-and-Variables.md - Review/modify:
github_docs/de-Funktionen.md,github_docs/en-Features.md - Review/modify:
github_docs/de-Benutzeroberflaeche.md,github_docs/en-User-Interface.md - Modify:
github_docs/de-Changelog.md - Modify:
github_docs/en-Changelog.md - Modify:
website/src/features/macros.md - Review/modify:
website/src/features/trx-qrg-synchronisation.md - Modify:
docs/PROJECT_CONTEXT.md - Modify:
docs/superpowers/specs/2026-11-04-live-beacon-control-design.md - Review:
github_docs/client_settings_window_beacon.png
Interfaces:
-
Consumes: completed behaviour from Tasks 1-4.
-
Produces: semantically aligned DE/EN user documentation and current durable project context.
-
Step 1: Update the targeted manual sections with session-only activation, five-minute minimum, activation prerequisites, source-specific reference/QSY rules, per-category tooltip and traffic colours.
-
Step 2: Update both changelogs compactly without inventing a version or release state.
-
Step 3: Update only website sections that already describe interval or TRX-QRG behaviour; do not duplicate the manual.
-
Step 4: Reconcile the older live-beacon design and
PROJECT_CONTEXTso they no longer claim activation persists or the one-minute minimum remains unchanged. -
Step 5: Inspect the existing Beacon settings screenshot and record replacement only if a visible value is now false; do not fabricate an image.
-
Step 6: Run all focused test classes from Tasks 1-4.
-
Step 7: Run the full build.
Run:
.\gradlew.bat build --console=plainExpected:
BUILD SUCCESSFULwith no failed tests. -
Step 8: Read every generated
*/build/test-results/**/TEST-*.xmlsummary and report test/failure/error/skipped totals rather than relying only on exit code. -
Step 9: Count PMD and SpotBugs XML findings per module from
<violationand<BugInstance>and identify findings touching changed production files. -
Step 10: Run
git diff --checkand inspectgit status --short. -
Step 11: Review the complete diff against the approved spec for unrelated cleanup, stale documentation, accidental protocol changes and user-authored changes.
-
Step 12: Perform a manual runtime check if safely possible: initial off state, valid/invalid category activation, manual rebase, external >5-kHz shutdown,
1/2, OFF tooltip, orange flash and persistent FAILED red.
Self-Review Result
- Every approved behaviour maps to Tasks 1-4; documentation and durable context map to Task 5.
- Exact types and controller entry points are defined once in the Interfaces blocks.
- Cross-template QRG dependencies, first external reference, non-SO2R routing, XML migration and FAILED colour priority are explicit tests.
- Plan size reflects five independently reviewable deliverables; no implementation bodies or unauthorised Git actions are included.