mirror of
https://github.com/praktimarc/kst4contest.git
synced 2026-11-15 00:52:31 +01:00
Harden beacon sessions and mark automatic messages
This commit is contained in:
1 parent
19bcdb428d
commit
f13fe54fde
63 files changed
+1905
-252
No files matched your search
@@ -0,0 +1,207 @@
|
||||
# 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 `MYQRG` must 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.
|
||||
- `FAILED` must 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 methods `resetSession(ChatPreferences)`, `setEnabled(Channel, boolean, ChatPreferences)`, `onManualQrgChanged(Channel, String, ChatPreferences)`, `onExternalQrgChanged(Channel, long, ChatPreferences)`, `onTemplateChanged(Channel, ChatPreferences)`, `recordSendFailure(Channel, boolean)` and `snapshot(ChatPreferences): BeaconStatusSnapshot`.
|
||||
- Produces: explicit `BeaconStatusSnapshot` DTO with aggregate state `OFF`, `PARTIAL`, `ON`, `FAILED`, plus main/second category DTOs carrying availability, enablement, failure and `InactiveReason`.
|
||||
|
||||
- [ ] **Step 1: Write parser tests** for `144.300`, `144,300`, `144.300.03`, `1296.123.000`, compact `144300`, compact `10368100`, 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=plain`
|
||||
|
||||
Expected: compilation failure because `BeaconFrequencyParser` does not exist.
|
||||
|
||||
- [ ] **Step 3: Implement `BeaconFrequencyParser`** with integer arithmetic and the QRG forms already used by the client; accept only a complete positive frequency that maps to a supported `Band`.
|
||||
- [ ] **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 `BeaconSessionStateTest` and 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 == 5` and session reset during controller/profile construction without reset on reconnect.
|
||||
|
||||
- [ ] **Step 1: Replace persistence expectations with failing tests** proving defaults are off, stored `true` is ignored, every full write stores both legacy/current enable tags as `false`, 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 `ChatInputActions` implementations 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()` and `onSecondBeaconTemplateApplied()`.
|
||||
- Produces: a `ChatInputActions`/`ChatInputState` manual-QRG commit method used by `ChatInputRow` instead 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 `ComposeChatInputActions`** while preserving focus/commit behaviour and existing displayed `ObservedValue` mirrors.
|
||||
- [ ] **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 `applyExternalQrg`** only 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 `BeaconStatusSnapshot` and policy send-failure methods.
|
||||
- Produces: optional structured beacon snapshot on `ThreadStateMessage`; other thread messages remain unchanged.
|
||||
- Produces: `BeaconStatusPresentation` mapping 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 `BeaconTask` record 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 `ThreadStatusButtonView`** and 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_CONTEXT`** so 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=plain`
|
||||
|
||||
Expected: `BUILD SUCCESSFUL` with no failed tests.
|
||||
|
||||
- [ ] **Step 8: Read every generated `*/build/test-results/**/TEST-*.xml` summary** 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 `<violation` and `<BugInstance>` and identify findings touching changed production files.
|
||||
- [ ] **Step 10: Run `git diff --check` and inspect `git 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.
|
||||
@@ -0,0 +1,96 @@
|
||||
# Automatic Chat Suffix 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:** Append the fixed suffix ` *a*` to every active KST4Contest autoanswer and beacon while leaving manual and protocol traffic unchanged.
|
||||
|
||||
**Architecture:** A package-private `AutomaticChatMessageFormatter` owns the suffix and performs unconditional appending. The active autoanswer and beacon producers call it explicitly; beacon validation keeps a 120-character payload allowance and validates the resulting 124-character maximum.
|
||||
|
||||
**Tech Stack:** Java 21, Kotlin 2.2, Gradle wrapper, JUnit 5, Mockito
|
||||
|
||||
**Spec:** `docs/superpowers/specs/2026-11-13-automatic-chat-suffix-design.md`
|
||||
|
||||
## Global Constraints
|
||||
|
||||
- The exact suffix is ` *a*`, including its leading space.
|
||||
- `[KST4C Automsg]` remains on autoanswers and remains the loop-prevention marker.
|
||||
- Suffix insertion is unconditional; duplicate `*a*` markers are allowed.
|
||||
- Beacon payloads remain limited to 120 characters; complete suffixed beacons are limited to 124 characters.
|
||||
- Manual messages and internal ON4KST frames must remain byte-for-byte unaffected by this feature.
|
||||
- No new dependency, setting, persistence field, version bump, staging, commit or publication action.
|
||||
|
||||
## Review Focus
|
||||
|
||||
- A 120-character beacon payload must be sent as exactly 124 characters after suffix insertion.
|
||||
- A 121-character beacon payload must still be rejected rather than producing a 125-character frame.
|
||||
- Text already ending in `*a*` must receive a second suffix without deduplication.
|
||||
- Autoanswer validation and cooldown accounting must still reject invalid text before consuming cooldown.
|
||||
- The existing prefix-based ping-pong guard and all manual send paths must remain unchanged.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Shared automatic-message formatter and autoanswers
|
||||
|
||||
**Files:**
|
||||
- Create: `core/src/main/java/kst4contest/controller/AutomaticChatMessageFormatter.java`
|
||||
- Create: `core/src/test/java/kst4contest/controller/AutomaticChatMessageFormatterTest.java`
|
||||
- Modify: `core/src/main/java/kst4contest/controller/MessageBusManagementThread.java`
|
||||
- Modify: `core/src/test/java/kst4contest/controller/MessageBusManagementThreadAutoAnswerDecisionTest.java`
|
||||
- Modify: `core/src/test/java/kst4contest/controller/MessageBusManagementThreadAutoAnswerTest.java`
|
||||
- Modify: `core/src/test/java/kst4contest/controller/MessageBusManagementThreadChatFrameTest.java`
|
||||
|
||||
**Interfaces:**
|
||||
- Produces: `AutomaticChatMessageFormatter.appendSuffix(String messageText): String`
|
||||
- Consumes: existing `ApplicationConstants.AUTOANSWER_PREFIX` and `On4KstProtocol.messageText(String)`
|
||||
|
||||
- [x] Add failing formatter tests for exact suffix insertion and intentional duplicate suffixes.
|
||||
- [x] Run `\.\gradlew.bat :core:test --tests "kst4contest.controller.AutomaticChatMessageFormatterTest"` and confirm the tests fail because the formatter is absent.
|
||||
- [x] Implement the package-private formatter with `SUFFIX = " *a*"` and unconditional appending.
|
||||
- [x] Update failing autoanswer and version-reply expectations so generic, QRG and `//VER` replies end in ` *a*` while retaining `[KST4C Automsg]`.
|
||||
- [x] Append before final autoanswer protocol validation and in the `//VER` construction path.
|
||||
- [x] Run the formatter and three focused autoanswer test classes and confirm zero failures.
|
||||
|
||||
### Task 2: Beacon suffix and length boundaries
|
||||
|
||||
**Files:**
|
||||
- Modify: `core/src/main/java/kst4contest/controller/ChatController.java`
|
||||
- Modify: `core/src/main/java/kst4contest/controller/BeaconTask.java`
|
||||
- Modify: `core/src/test/java/kst4contest/controller/On4KstProtocolTest.java`
|
||||
- Modify: `core/src/test/java/kst4contest/controller/BeaconTaskTest.java`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: `AutomaticChatMessageFormatter.appendSuffix(String)` from Task 1
|
||||
- Produces: beacon payload validation at 120 characters and final-message validation at 124 characters
|
||||
|
||||
- [x] Add failing tests asserting that each queued beacon ends in ` *a*`, a 120-character payload produces a 124-character message, and a 121-character payload is rejected.
|
||||
- [x] Run `\.\gradlew.bat :core:test --tests "kst4contest.controller.On4KstProtocolTest" --tests "kst4contest.controller.BeaconTaskTest"` and confirm the new assertions fail.
|
||||
- [x] Separate the 120-character payload limit from the 124-character final beacon limit in `ChatController`.
|
||||
- [x] Append the suffix only after macro resolution and payload validation, then validate the final message before queueing.
|
||||
- [x] Run the focused beacon tests and confirm zero failures.
|
||||
|
||||
### Task 3: Targeted documentation and verification
|
||||
|
||||
**Files:**
|
||||
- Modify: `github_docs/de-Konfiguration.md`
|
||||
- Modify: `github_docs/en-Configuration.md`
|
||||
- Modify: `github_docs/de-Makros-und-Variablen.md`
|
||||
- Modify: `github_docs/en-Macros-and-Variables.md`
|
||||
- Modify: `github_docs/de-Funktionen.md`
|
||||
- Modify: `github_docs/en-Features.md`
|
||||
- Modify: `github_docs/de-Changelog.md`
|
||||
- Modify: `github_docs/en-Changelog.md`
|
||||
- Modify: `website/src/features/automatic-replies.md`
|
||||
- Modify: `website/src/features/macros.md`
|
||||
- Modify: `docs/PROJECT_CONTEXT.md`
|
||||
|
||||
**Interfaces:**
|
||||
- Consumes: implemented suffix and length behaviour from Tasks 1 and 2
|
||||
- Produces: aligned German/English user documentation and durable current-state context
|
||||
|
||||
- [x] Update examples and behavioural text so autoanswers retain `[KST4C Automsg]` and all automated chat messages end in ` *a*`.
|
||||
- [x] Document the 120-character beacon payload and 124-character transmitted-message limits in German and English.
|
||||
- [x] Record the fixed suffix rule in the changelogs and `docs/PROJECT_CONTEXT.md` without changing unrelated documentation.
|
||||
- [x] Run the focused core tests from Tasks 1 and 2, then `\.\gradlew.bat build`.
|
||||
- [x] Read every Gradle test-result XML summary and report failures, errors and skipped tests; inspect PMD and SpotBugs XML counts produced by the build.
|
||||
- [x] Run the website test/build commands defined by the current `website/package.json` if the changed website pages participate in that build.
|
||||
- [x] Run `git diff --check` and review the complete scoped diff for accidental changes; perform no Git publication action.
|
||||
@@ -2,6 +2,10 @@
|
||||
|
||||
Freigegeben von Marc am 2026-11-04.
|
||||
|
||||
> Durch das freigegebene [Beacon Session Safety Design](2026-11-11-beacon-session-safety-design.md)
|
||||
> abgelöst, soweit Aktivierungspersistenz, Mindestintervall, QRG-Sicherung und Chip-Darstellung
|
||||
> betroffen sind.
|
||||
|
||||
## Ziel
|
||||
|
||||
Die beiden Beacon-Aktivierungsschalter wirken in der laufenden Sitzung sofort. Der gemeinsame
|
||||
@@ -11,19 +15,19 @@ gemeinsam ein- oder ausschalten.
|
||||
|
||||
## Verbindliches Verhalten
|
||||
|
||||
- Die gespeicherten Beacon-Zustände werden beim Profilstart geladen.
|
||||
- Nach einem Programmstart und Profilwechsel sind beide Beacon-Zustände ausgeschaltet.
|
||||
- Ein normaler ON4KST-Reconnect innerhalb derselben Programmsitzung verändert sie nicht.
|
||||
- Ein Klick auf eine Beacon-Checkbox wirkt sofort im Speicher und damit beim nächsten
|
||||
regulären Timerlauf.
|
||||
- Aktivieren sendet nicht sofort und startet den Timer nicht neu.
|
||||
- Erst **Einstellungen speichern** schreibt die Zustände in die Profil-XML. Ohne explizites
|
||||
Speichern gilt die Änderung nur für die laufende Sitzung.
|
||||
- Aktivierungszustände werden nicht in die Profil-XML übernommen; ein vollständiger
|
||||
Einstellungsspeichervorgang schreibt die kompatiblen Aktivierungs-Tags als `false`.
|
||||
- Beacon-Text und Intervall bleiben normale Settings-Entwurfswerte. Das Intervall startet den
|
||||
Timer erst beim Anwenden neu.
|
||||
- Der Status-Chip zeigt `off`, `1/2` oder `on` für die aktuell nutzbaren Kategorien.
|
||||
- Der Status-Chip zeigt `off`, `1/2`, `on` oder `FAILED` für die aktuell nutzbaren Kategorien.
|
||||
- Sind alle nutzbaren Beacons aus, aktiviert ein Chip-Klick alle nutzbaren Beacons. Ist
|
||||
mindestens eine nutzbare Beacon an, deaktiviert der Klick alle nutzbaren Beacons.
|
||||
- Bei deaktiviertem zweiten Chat schaltet der Chip nur die Hauptkategorie und verändert das
|
||||
gespeicherte Flag der zweiten Kategorie nicht.
|
||||
- Bei deaktiviertem zweiten Chat schaltet der Chip nur die Hauptkategorie.
|
||||
- Chip und geöffnetes Settings-Fenster zeigen denselben beobachtbaren Zustand.
|
||||
- Nur der Beacon-Chip wird anklickbar. Andere Thread-Status-Chips bleiben reine Anzeigen.
|
||||
|
||||
@@ -32,15 +36,15 @@ gemeinsam ein- oder ausschalten.
|
||||
- Beide Kategorien behalten eigene Aktivierungsflags und Texte.
|
||||
- Der zweite Beacon-Versand setzt weiterhin einen aktivierten zweiten Chat voraus.
|
||||
- Nachrichtenvalidierung, TX-Warteschlange und ON4KST-Protokoll bleiben unverändert.
|
||||
- Mindestintervall, gemeinsamer Timer und Intervall-Persistenz bleiben unverändert.
|
||||
- Gemeinsamer Timer und Intervall-Persistenz bleiben unverändert; das Mindestintervall beträgt
|
||||
fünf Minuten.
|
||||
- Profilzustände werden nicht miteinander vermischt.
|
||||
|
||||
## Technischer Ansatz
|
||||
|
||||
Die vorhandenen `ChatPreferences`-Getter und -Setter bleiben die kompatible Schnittstelle,
|
||||
werden für die beiden Aktivierungsflags aber auf thread-sichere beobachtbare Werte gestellt.
|
||||
Der Settings-Tab liest diese Werte als Compose-State und schreibt Aktivierungen direkt durch
|
||||
den Controller. Der Controller veröffentlicht nach jeder manuellen Umschaltung einen
|
||||
zusammengefassten `MyBeacon`-Status. `BeaconTask` meldet nach einem Timerlauf ebenfalls genau
|
||||
diesen zusammengefassten Status. `ThreadStatusButtons` erhält eine optionale Aktion pro
|
||||
Quelle; die Anwendung registriert sie nur für `MyBeacon`.
|
||||
`BeaconSessionState` ist die maßgebliche sitzungsbezogene Quelle für Aktivierung,
|
||||
QRG-Referenzen, Abschaltgründe und Fehler. Die vorhandenen beobachtbaren
|
||||
`ChatPreferences`-Werte bleiben die kompatible Projektion für Settings und UI. Controller und
|
||||
`BeaconTask` veröffentlichen einen strukturierten `BeaconStatusSnapshot`, aus dem Chip-Text,
|
||||
Tooltip und die Beacon-spezifische Farbgebung entstehen. Details der QRG-Prüfung und
|
||||
Darstellung stehen im ablösenden Design vom 2026-11-11.
|
||||
@@ -0,0 +1,128 @@
|
||||
# Beacon Session Safety Design
|
||||
|
||||
Freigegeben von Marc am 2026-11-11.
|
||||
|
||||
## Ziel
|
||||
|
||||
Die Beacon-Funktion darf nach einem Programmstart oder Profilwechsel nicht aus einer
|
||||
gespeicherten Aktivierung weiterlaufen. Jeder Beacon wird in der laufenden Sitzung bewusst
|
||||
eingeschaltet, hält seine eigene Kategorie-QRG als Referenz fest und schaltet nur bei einer
|
||||
automatisch von UCXLog oder Win-Test gemeldeten Abweichung von mehr als 5 kHz ab.
|
||||
|
||||
Der zusammengefasste `MyBeacon`-Chip macht Aktivierung, Teilaktivierung, automatische
|
||||
Abschaltung und Versandfehler ohne Umweg über das Einstellungsfenster erkennbar.
|
||||
|
||||
## Verbindliches Verhalten
|
||||
|
||||
### Intervall und Sitzung
|
||||
|
||||
- Das kleinste zulässige gemeinsame Beacon-Intervall beträgt fünf Minuten.
|
||||
- Genau fünf Minuten sind zulässig. Kleinere geladene Werte werden im Speicher auf fünf
|
||||
Minuten normalisiert; die mitgelieferte Konfiguration verwendet für beide Kategorien fünf.
|
||||
- Nach jedem Programmstart und Profilwechsel sind beide Beacons ausgeschaltet.
|
||||
- Ein Disconnect oder Reconnect innerhalb derselben Programmsitzung verändert ihre
|
||||
Aktivierung nicht.
|
||||
- Die Aktivierungsflags werden nicht aus XML gelesen. Die vorhandenen XML-Elemente bleiben
|
||||
für die Dateikompatibilität erhalten, werden aber immer mit `false` geschrieben.
|
||||
|
||||
### Aktivierung und benötigte QRG-Werte
|
||||
|
||||
- `MYQRG` und `MYQRGSHORT` benötigen eine gültige Haupt-QRG.
|
||||
- `SECONDQRG` benötigt eine gültige zweite QRG.
|
||||
- Diese Voraussetzung gilt unabhängig davon, in welchem der beiden Bakentexte die Variable
|
||||
vorkommt.
|
||||
- Fehlt ein benötigter Wert oder ist er ungültig, bleibt der betroffene Beacon aus. Sein
|
||||
kategorieweiser Status nennt die fehlende oder ungültige QRG als Grund.
|
||||
- Der gemeinsame Chip aktiviert beide nutzbaren Kategorien unabhängig. Ein gültiger Beacon
|
||||
wird eingeschaltet, auch wenn der andere abgelehnt wird; der Chip zeigt dann `1/2`.
|
||||
- Ein Text ohne QRG-Platzhalter darf ohne bekannte QRG aktiviert werden.
|
||||
- Geprüft wird der bereits angewendete Text, nicht ein noch offener Settings-Entwurf.
|
||||
|
||||
### Kategoriebezogene Referenz-QRG
|
||||
|
||||
- Beim Einschalten übernimmt jeder Beacon die aktuell gültige QRG seiner eigenen Kategorie
|
||||
als Referenz.
|
||||
- Ist keine gültige Kategorie-QRG vorhanden und benötigt der Text sie nicht, wird die erste
|
||||
später gültige manuelle oder externe QRG zur Referenz.
|
||||
- Eine gültige manuelle QRG-Eingabe ist eine bewusste Operatorentscheidung. Sie deaktiviert
|
||||
keinen Beacon und verschiebt die Referenz der zugehörigen Kategorie.
|
||||
- Eine ungültige manuelle Eingabe deaktiviert jeden aktiven Beacon, dessen angewendeter Text
|
||||
den betroffenen QRG-Wert benötigt. Benötigt sein Text ihn nicht, bleibt der Beacon aktiv;
|
||||
nur die Referenz seiner Kategorie wird gelöscht.
|
||||
- Ausschließlich QRG-Übernahmen von UCXLog `RadioInfo` und Win-Test `STATUS`, für die der
|
||||
bestehende QRG-Abgleich aktiv ist, dürfen automatisch abschalten.
|
||||
- Der Vergleich erfolgt in ganzzahligen Hz gegen die aktuelle Referenz. Genau 5.000 Hz sind
|
||||
zulässig; erst eine größere Abweichung schaltet ab.
|
||||
- Ohne Referenz setzt die erste externe QRG nur die Referenz und schaltet nicht ab.
|
||||
- Ohne SO2R betrifft eine externe Änderung von `MYQRG` ausschließlich die Hauptbake. Mit
|
||||
SO2R folgt Radio 1 `MYQRG` und Radio 2 `SECONDQRG`.
|
||||
|
||||
### Status und Tooltip
|
||||
|
||||
- Der Status wird als strukturierter Snapshot aus `core` geliefert. Übersetzte Anzeigetexte
|
||||
entstehen ausschließlich in `app-desktop`.
|
||||
- Der Snapshot kennt `OFF`, `PARTIAL`, `ON` und `FAILED` sowie den Zustand beider Kategorien.
|
||||
- Der Tooltip zeigt je Kategorie den aktuellen Zustand oder letzten Abschalt-/Ablehnungsgrund:
|
||||
noch nicht in dieser Sitzung aktiviert, durch den Benutzer ausgeschaltet, fehlende oder
|
||||
ungültige Haupt-QRG, fehlende oder ungültige zweite QRG oder automatische QRG-Abweichung
|
||||
von mehr als 5 kHz.
|
||||
- Der QRG-Grund nennt keine konkreten Frequenzen.
|
||||
- Timeraktivität überschreibt den kategorieweisen Grund nicht.
|
||||
- Ein erfolgreicher späterer Lauf löscht einen früheren Versandfehler der Kategorie.
|
||||
|
||||
### Verkehrsfarben des `MyBeacon`-Chips
|
||||
|
||||
- `on` und `1/2`: Hintergrund `#238636`, Rand `#56D364`, weiße Schrift.
|
||||
- Der bestehende 200-ms-Aktivitätsblitz verwendet Hintergrund `#FFB300`, Rand `#FFE082`
|
||||
und Schrift `#1B1B1B`.
|
||||
- `off`: neutraler Theme-Hintergrund mit `#D50000` im hellen beziehungsweise `#FF6B6B` im
|
||||
dunklen Design.
|
||||
- `FAILED`: dauerhaft `#D50000` mit Rand `#FF6B6B` und weißer Schrift.
|
||||
- `FAILED` unterdrückt den orangefarbenen Aktivitätsblitz.
|
||||
- Andere Status-Chips behalten ihr heutiges Theme-Verhalten.
|
||||
|
||||
## Technischer Ansatz
|
||||
|
||||
Eine controllerseitige `BeaconSessionState`-Komponente koordiniert den Zustand beider
|
||||
Kategorien. Die beobachtbaren Flags in `ChatPreferences` bleiben kompatible UI-Projektion,
|
||||
aber kein persistenter Produktzustand. Die Komponente verwaltet Referenzen, kategorieweise
|
||||
Gründe und Versandfehler und ist der einzige Produktionsschreibpfad der Aktivierungsflags.
|
||||
|
||||
Manuelle QRG-Eingaben und externe Logger-Meldungen verwenden getrennte Controller-Einstiege.
|
||||
UCXLog und Win-Test übergeben die bereits ermittelte Kategorie, den formatierten Anzeigewert
|
||||
und die genaue Frequenz in Hz innerhalb der bestehenden `UiDispatcher`-Grenze. Damit ist die
|
||||
Herkunft explizit; Listener, Thread-lokale Markierungen oder erratene Ursprünge sind unnötig.
|
||||
|
||||
Der vorhandene `BeaconTask` bleibt für Auflösung, Validierung und Einreihung in die reguläre
|
||||
TX-Warteschlange verantwortlich. Er meldet je Kategorie Erfolg oder Fehler an den
|
||||
Sitzungszustand zurück. Ein DTO im gemeinsamen Model transportiert den strukturierten Status
|
||||
über den bestehenden Threadstatus-Kanal; Compose übersetzt Gründe und wählt die Chipfarben.
|
||||
|
||||
## Unveränderte Grenzen
|
||||
|
||||
- Beide Kategorien teilen einen Timer und besitzen getrennte Aktivierung und Texte.
|
||||
- Der zweite Versand setzt weiterhin eine verfügbare zweite Chatkategorie voraus.
|
||||
- Aktivieren sendet nicht sofort und startet den Timer nicht neu.
|
||||
- Der erste Timerlauf nach dem Verbindungsaufbau bleibt bei ungefähr zehn Sekunden.
|
||||
- Nachrichtenauflösung, Längenlimit, TX-Warteschlange und ON4KST-Framing bleiben unverändert.
|
||||
- Die Chip-Texte `off`, `1/2`, `on` und `FAILED` und seine gemeinsame Klickfunktion bleiben.
|
||||
- Es gibt keine neue Produktionsabhängigkeit, kein neues persistentes Schema und keine
|
||||
Änderung eines externen Protokolls.
|
||||
|
||||
## Berücksichtigte Ansätze
|
||||
|
||||
1. **Gewählt:** controllerseitiger Sitzungszustand mit expliziten manuellen und externen
|
||||
Ereignissen. Das hält Policy, Referenzen und Gründe zusammen und bleibt testbar.
|
||||
2. **Verworfen:** Flags in Preferences, Referenzen in einem Guard und Gründe in `BeaconStatus`.
|
||||
Der zusammengehörige Zustand wäre auf mehrere Besitzer verteilt.
|
||||
3. **Verworfen:** Beobachtung der QRG-Werte mit temporärer Herkunftsmarkierung. `MutableValue`
|
||||
trägt keine Quelle; die asynchrone UI-Weitergabe machte diesen Ansatz fragil.
|
||||
|
||||
## Dokumentationswirkung
|
||||
|
||||
Die Änderung ist benutzerseitig sichtbar, betrifft Laufzeit/Persistenz sowie die bestehende
|
||||
UCXLog-/Win-Test-Integration. Geprüft und bei Bedarf angepasst werden die relevanten
|
||||
deutschen und englischen Konfigurations-, Workflow-, Variablen-, Funktions- und
|
||||
Benutzeroberflächenabschnitte, Changelog, Website-Texte, `docs/PROJECT_CONTEXT.md` und der
|
||||
ältere Live-Beacon-Entwurf. README und verbundene Projekte sind voraussichtlich nicht
|
||||
betroffen.
|
||||
@@ -0,0 +1,27 @@
|
||||
# Automatic Chat Suffix Design
|
||||
|
||||
## Intent
|
||||
|
||||
Every user-visible chat message generated and sent automatically by KST4Contest must end with the fixed suffix ` *a*`. The marker makes automatic traffic recognisable without changing manual messages or ON4KST control traffic.
|
||||
|
||||
## Behaviour
|
||||
|
||||
- Append ` *a*` to generic private autoanswers, QRG autoanswers, `//VER` replies and both beacon categories.
|
||||
- Keep the existing `[KST4C Automsg]` prefix on every autoanswer and continue using it for the reply-loop guard.
|
||||
- Append the suffix unconditionally after text preparation. A configured text which already ends in `*a*` therefore receives another ` *a*`.
|
||||
- Preserve the existing 120-character beacon payload allowance. The complete transmitted beacon, including the four-character suffix, may contain up to 124 characters.
|
||||
|
||||
## Non-goals
|
||||
|
||||
- Do not mark manually sent messages, snippets or manually triggered CQ messages.
|
||||
- Do not alter login, keepalive, ON4KST command or other internal protocol frames.
|
||||
- Do not change cooldowns, category routing, beacon activation, QRG safeguards, timer behaviour, persistence or settings UI.
|
||||
- Do not revive or refactor the unused legacy `ScoreboardUpdateTask`.
|
||||
|
||||
## Selected Approach
|
||||
|
||||
Use one package-private formatter in the controller package to append the fixed suffix. Call it explicitly from the active autoanswer and beacon construction paths. This keeps the scope visible and prevents a broad writer-layer rule from modifying manual messages or raw protocol frames.
|
||||
|
||||
## Validation and Documentation
|
||||
|
||||
Validate autoanswers after suffix insertion. Validate beacon payloads against 120 characters, append the suffix, then validate the final text against 124 characters. Update focused tests, the German and English manuals, relevant website feature pages, changelogs and durable project context.
|
||||
Reference in new issue
Block a user