67413 Commits
Author SHA1 Message Date
Axel Uhl 6cb7d291e5 bug6316: add junit-platform-suite bundles to target platform for Tycho 5
Tycho 4.0.x implicitly contributed the JUnit Platform "suite" bundles to the
tycho-surefire test runtime, so our target platform never had to provide
org.junit.platform.suite.api itself. Tycho 5.0.4 no longer injects them, which
broke test resolution for every *.test bundle with:

  Cannot resolve project dependencies:
    You requested to install 'java.package; org.junit.platform.suite.api
    [1.0.0,2.0.0)' but it could not be found

The 2024-12 release repository we already reference for JUnit 5 (Platform
1.11.3 / Jupiter 5.11.3) also provides the split suite artifacts, so add them
from the same repository and include them as bundles:

  junit-platform-suite-api / -commons / -engine (+ .source) 1.11.3

suite-engine is included as well, at effectively no cost, to also support
running JUnit Platform @Suite classes via the suite engine.

Assisted-By: Claude Opus 4.8
main-202610070035
2026-10-06 19:46:29 +02:00
Axel Uhl 3cda4269cd Merge branch 'eclipse-main' into bug6316 2026-10-06 18:10:51 +02:00
Axel Uhl b7f5edf70e Bump pinned Apache Maven from 3.9.11 to 3.9.16
3.9.16 is the newest release on the Maven 3.9.x line (which Tycho 4.0.x
requires) and is the version the GitHub runner image shipped as default
before image 20261004.327.1 bumped the default to the Tycho-incompatible
3.10.0. Pinning the image's former default avoids a per-run download.

Assisted-By: Claude Opus 4.8
main-202610061922
2026-10-06 18:08:33 +02:00
Axel Uhl defa035a70 bug6316: upgrade Eclipse Tycho from 4.0.12 to 5.0.4
Tycho 4.0.x is end-of-life (4.0.13 was the final release); upstream
recommends migrating to the 5.x main line. Tycho 5.x requires Java 21+ as
its runtime VM, which release.yml already satisfies (Maven runs on JDK
25), and still targets the Maven 3.9.x line, so the existing Maven 3.9.11
pin remains valid.

Only java/pom.xml is bumped. The standalone java/com.amazon.aws.aws-java-api
module is deliberately left at Tycho 1.7.0: it builds its local p2 repo
under JDK 8, and Tycho 5.x cannot run on Java 8.

This is an initial probe to see how the release build reacts; serialization/
replication validation (see Bugzilla 3859 note in java/pom.xml) still pending.

Assisted-By: Claude Opus 4.8
2026-10-06 17:40:15 +02:00
Axel Uhl 2550cecea3 Pin Apache Maven to 3.9.11 for Tycho 4.0.x compatibility
The GitHub-hosted ubuntu runner image bumped its default Apache Maven
from 3.9.x to 3.10.0 (image ubuntu24/20261004.327). Tycho 4.0.x requires
the Maven 3.9.x line; under Maven 3.10.0 Tycho's Sisu/Guice component
wiring fails at extension bootstrap with "No implementation for
MavenTargetLocationFactory was bound" while provisioning
DefaultTargetPlatformFactory, breaking the build.

Pin Maven to 3.9.11 via setup-maven in every workflow that runs Maven
(release, build-aws-sdk-p2, build-sailing-base-p2, build-jetty-p2) so the
build no longer depends on whatever Maven the runner image ships. The
action is SHA-pinned consistently (stCarolas/setup-maven v5.1).

Assisted-By: Claude Opus 4.8
2026-10-06 17:33:04 +02:00
Axel Uhl b516c7957e fixed typo in wiki tutorials home.md main-202610061400 2026-10-06 13:13:21 +02:00
Axel Uhl 179a3ee25c Merge remote-tracking branch 'eclipse/main' into eclipse-main 2026-10-06 13:05:21 +02:00
Axel Uhl ab8a04f8e7 Merge branch 'eclipse-main' into hyperspace 2026-10-06 13:04:14 +02:00
Axel Uhl 024d1d65be hyperspace: raise Gradle Java source/target from 8 to 17
JDK 21 flagged source/target 8 as obsolete. The Gradle app build already
runs under JDK 17 (buildAndUpdateProduct.sh / release.yml), and AGP 8.7
permits Java 17, so bump sourceCompatibility/targetCompatibility and the
ext compat vars to 17. This covers the 4 mobile modules and the 8 java/
library subprojects compiled by Gradle; the separate Tycho/Maven OSGi
build is unaffected (its BREE pins it to Java 8 independently).

Assisted-By: Claude (claude-opus-5-5)
2026-10-06 11:34:23 +02:00
Axel Uhl 51f9c0ee6b sorted links in Wiki/tutorial support.sapsailing.com section main-202610060027 2026-10-06 00:09:06 +02:00
axeluhl fa9a59d324 Updated smartphone-tracking.md (markdown) 2026-10-05 23:59:05 +02:00
axeluhl 551cc68e88 Updated smartphone-tracking.md (markdown) 2026-10-05 23:58:56 +02:00
Axel Uhl 5e802fbf46 Merge pull request #6314 from janhamann/wiki/smartphone-tracking-guide
wiki: Add step-by-step guide for event hosts on Smartphone Tracking;
2026-10-05 23:29:45 +02:00
Axel Uhl cdefc5f107 Revise smartphone tracking documentation for clarity
Updated instructions for smartphone tracking, including clarifications on server requirements, competitor registration, and tracking management. Enhanced details on using the Race Manager app and handling competitor invitations.
2026-10-05 23:26:06 +02:00
Axel Uhl baa80a0126 fixed link from planning.md to smartphone-tracking-general 2026-10-05 21:01:47 +02:00
Axel Uhl 98a09bd4e7 Merge remote-tracking branch 'eclipse/main' into eclipse-main 2026-10-05 21:01:42 +02:00
Jan Hamann 688648bae3 wiki: Add step-by-step guide for event hosts on Smartphone Tracking;
New page wiki/howto/eventmanagers/smartphone-tracking.md walks event hosts
through setting up smartphone tracking in the order they actually do it:
event structure, denoting races (incl. the race name prefix dialog),
competitor registration, course layout, the three ways to bring phones in
(device mapping QR code, e-mail invitation, open-regatta registration link),
starting tracking and typical problems. Button and dialog names were taken from the
current Admin Console string messages where available.

Linked from Home.md; the two older pages
info/mobile/smartphone-tracking-steps and -general now point to it.

Assisted-By: Claude Code with Claude Sonnet 5.5
2026-10-05 19:49:24 +02:00
Axel Uhl 22e789d921 Bind httpprinter debug socket to loopback only
Fixes CodeQL py/bind-socket-all-network-interfaces by binding the
local debug HTTP printer to 127.0.0.1 instead of all interfaces
(host = ''). The script is a small local debugging utility, so
restricting it to loopback does not affect its intended use.

Assisted-By: Claude Opus 4.8
main-202610051921
2026-10-05 18:37:26 +02:00
Axel Uhl 42bdfb2cb4 checking MDI URL for trusted domains also from admin console UI 2026-10-05 18:18:24 +02:00
ospo-renovate[bot] b1ec9c1908 Migrate config renovate.json main-202610051420 2026-10-05 13:42:19 +02:00
Axel Uhl 6c397255bd Merge pull request #6313 from eclipse-sailing-analytics/hyperspace
Upgrading targetSDK to 36, handling edge-to-edge / insets and opting out of predictive "Back" button
2026-10-05 13:27:46 +02:00
Axel Uhl b3ab8d8d62 Merge pull request #36 from SAP-Sailing-Analytics/renovate/lock-file-maintenance
Lock file maintenance
2026-10-05 12:12:42 +02:00
ospo-renovate[bot] 22dd5aef07 Lock file maintenance 2026-10-05 10:12:28 +00:00
Axel Uhl b70837f803 Merge pull request #35 from SAP-Sailing-Analytics/renovate/rubygems-sinatra-vulnerability
Update dependency sinatra to v4 [SECURITY]
2026-10-05 12:12:00 +02:00
ospo-renovate[bot] afe1eb54a7 Update dependency sinatra to v4 [SECURITY] 2026-10-05 10:11:51 +00:00
Axel Uhl 88a8587f91 Merge pull request #34 from SAP-Sailing-Analytics/renovate/rubygems-webrick-vulnerability
Update dependency webrick to v1.8.2 [SECURITY]
2026-10-05 12:11:32 +02:00
Axel Uhl 4bcf7cabad Merge pull request #33 from SAP-Sailing-Analytics/renovate/rubygems-rack-vulnerability
Update dependency rack to v2.2.23 [SECURITY]
2026-10-05 12:11:15 +02:00
Axel Uhl 05f8a658c9 hyperspace: fix Play Services version skew crashing QR scanner (Buoy Pinger)
After the Android 16 / AGP 8 / JDK 21 upgrades, opening the QR code scanner
in the Buoy Pinger app crashed with NoClassDefFoundError for the internal
GMS classes zzdkv / zzbgl when constructing BarcodeDetector.Builder.

Root cause: play-services-maps:17.0.1 forced play-services-basement/base up to
17.0.0 in the Buoy Pinger graph, while play-services-vision stayed at 12.0.1.
Vision 12.0.1 references internal classes that existed in basement 12.x but
were removed in 17.0.0, so they fail to resolve at runtime. Race Manager was
unaffected only because it pulls no maps dependency, keeping its whole GMS
stack at 12.x.

Fix (preserving minSdk 16 for old tracking/pinging hardware):
- Bump play-services-vision 12.0.1 -> 19.0.0. Vision 19.0.0 depends on
  basement/base 17.0.0 (the versions maps 17.0.1 already pulls) and keeps
  minSdk 14; the BarcodeDetector API is unchanged, so no barcode code changes.
- Bump play-services-location 12.0.1 -> 21.0.1 so Race Manager's fused-location
  calls do not hit the same basement mismatch once vision raises basement; it
  resolves to basement 18.1.0 (minSdk 14), consistent across all three apps.
- Modernize WindFragment's deprecated LocationRequest API (create()/setPriority/
  setInterval/setFastestInterval and LocationRequest.PRIORITY_HIGH_ACCURACY) to
  LocationRequest.Builder with Priority.PRIORITY_HIGH_ACCURACY.

Verified: all three app modules (Buoy Pinger, Tracking, Race Manager) compile
cleanly against the bumped versions, with no location deprecation warnings.
Runtime QR scan not yet smoke-tested on a device.

Assisted-By: Claude Opus 4.8 (claude-opus-5-5)
2026-10-05 11:40:26 +02:00
Axel UhlandCopilot bd7ce48e65 hyperspace: target Android 16 (API level 36) as required by Google Play
Since August 31, 2026 Google Play rejects app updates not targeting API
level 36 ("Target SDK of artifact is too low: <versionCode>").

Build:
- AGP 8.7.3 -> 8.13.2 (8.7 supports at most API 35; 8.13 supports 36.1
  and works with our Gradle 8.14.3 / JDK 21)
- compileSdk 33 -> 36, targetSdk 35 -> 36, buildTools 34.0.0 -> 36.0.0
- azure-pipelines/main.yml: androidVersion 35 -> 36

Apps (all via the shared library, so all apps get it):
- Predictive back: opt out with android:enableOnBackInvokedCallback="false"
  so that the existing onBackPressed() overrides and KEYCODE_BACK handling
  keep being invoked on Android 16.
- Large screens: temporarily opt out of ignored orientation/resizability
  restrictions on sw600dp displays with
  PROPERTY_COMPAT_ALLOW_RESTRICTED_RESIZABILITY (ignored from API 37 on).
- Edge-to-edge, enforced without opt-out for targetSdk 36: new
  SystemBarInsetsHandler, registered by LoggableApplication on Android 15+
  devices, pads each activity's content view by the system bar, display
  cutout and (unless adjustPan/adjustNothing) keyboard insets so content
  no longer hides behind the status and navigation/button bar, and fills
  the status bar area with the theme's colorPrimaryDark as before.

Document the androidVersion / Play target API requirement in
building-and-deploying.md.

Assisted-By: Claude Opus 5.5

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-05 01:35:05 +02:00
ospo-renovate[bot] 9c5ba791b8 Update dependency webrick to v1.8.2 [SECURITY] 2026-10-04 21:47:52 +00:00
ospo-renovate[bot] 3d1b2fd10d Update dependency rack to v2.2.23 [SECURITY] 2026-10-04 21:47:40 +00:00
Axel Uhl c37c5f5502 bug6312: allow operators to set the initial admin password via SAILING_ADMIN_INITIAL_PASSWORD
When a fresh instance starts with an empty security store (one that does
not replicate its security service from a master), initEmptyStore() creates
the default "admin" user. Until now this always used the well-known password
"admin", created the account without enforcing a strong password, and logged
the password in cleartext.

Operators can now set the SAILING_ADMIN_INITIAL_PASSWORD environment variable
to supply the initial admin password. When set to a non-blank value, that
value is used and a strong password is enforced; the password value is never
logged. When unset, the previous "admin" default is kept for backward
compatibility (test / docker-deploy / CI) but a security warning is logged
recommending the variable be set, and noting that future versions may require
it. The cleartext password is no longer logged in either case.

This is opt-in and non-breaking; forcing the variable is left to a later
change after a deprecation period.

Assisted-By: Claude (Opus)
2026-10-04 23:00:56 +02:00
Axel Uhl b2632c3882 Stop logging OAuth tokens, secrets, and request tokens
OAuthRealm and SecurityServiceImpl logged OAuth credentials in cleartext:
the access token, its token value, secret, and raw provider response, as
well as the request token and verifier. These are sensitive values that
should never reach log files, where they widen the blast radius if logs
are leaked or aggregated.

Replace them with non-sensitive success/failure messages that identify
the provider only. The request-token/verifier lines are also dropped to
FINE since they carry no diagnostic value beyond the provider name.

Assisted-By: Claude (Opus)
2026-10-04 22:47:17 +02:00
Axel Uhl 46222d2d65 added configuration/listSailingAnalyticsServers script;
with valid AWS credentials in the env vars (see awsmfa) this script
will use findSailingAnalyticsServers and show the process configurations
found on them under /home/sailing/servers/*
2026-10-04 21:00:49 +02:00
Axel UhlandCopilot 34e3c45de8 hyperspace: rename release APKs to NAAS allow-listed names in Fastlane
Central Signing failed with HTTP 406 ("The requested list doesn't exist in
the library files") after 9e17c0b453 switched files2sign.json to the AGP 8
default <module>-release-unsigned.apk names: Non-ABAP Assembly allow-lists
the APK file name together with group/artifact ID per MoMa assembly, and
the allow-listed names are <module>-unsigned.apk.

Restore the allow-listed names in files2sign.json and let the releaseBuild
lane rename the APKs reported by the gradle action from
*-release-unsigned.apk to *-unsigned.apk, failing the lane if none is
found. This replaces the applicationVariants outputFileName rename dropped
in the AGP 8 migration without relying on the deprecated Variant API that
AGP 9 removes. Update building-and-deploying.md accordingly.

Assisted-By: Claude Opus 5.5

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-04 20:48:26 +02:00
Axel UhlandCopilot 9e17c0b453 hyperspace: match files2sign.json APK names to AGP 8 default output names
The AGP 8 migration (5a78d3e8e3) dropped the applicationVariants
outputFileName rename that stripped "-release" from the APK name, so the
build now produces <module>-release-unsigned.apk. The central signing stage
of the sap-mobile-pipeline template signs Binaries/Android/<file> literally
as listed in files2sign.json, which still named <module>-unsigned.apk and
failed with "File does not exist". Point files2sign.json at the AGP default
names instead of reintroducing the deprecated Variant API rename, which
AGP 9 removes. Document the naming requirement and update the JDK version
in building-and-deploying.md.

Assisted-By: Claude Opus 5.5

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
2026-10-04 20:15:34 +02:00
Axel Uhl 38f7f179bd Merge pull request #6311 from eclipse-sailing-analytics/hyperspace
Upgrading Android/Gradle to work with JDK 21
main-202610041745
2026-10-04 17:00:53 +02:00
Axel Uhl 9bc3a67c12 Bump sapGradleVersion to 8.14.3 to match the Gradle wrapper
Assisted-By: Claude Opus 4.8 (claude-opus-5-5)
2026-10-02 21:17:37 +02:00
Axel Uhl ffec282970 Merge branch 'eclipse-main' into hyperspace 2026-10-02 21:11:13 +02:00
Axel Uhl c8e7076053 Fix fatal lintDebug errors so Android apps stay Play-Store publishable under AGP 8
Under AGP 8 the CI 'build' task runs lintDebug as fatal. After the JDK 21 /
Gradle 8.14.3 / AGP 8.7.3 migration this surfaced several blockers that the
old Fastlane assembleRelease path never ran lint against.

Google Play SDK Index (RiskyLibrary):
- play-services-maps 12.0.1 is flagged as outdated and blocks publishing.
  Split the shared play_services version variable into per-artifact versions
  (maps/location/vision) so modules can diverge, and bump maps to 17.0.1 (its
  minSdk floor is still 14, so no app needs a higher minSdk).
- Move play-services out of the shared library: it now exposes vision only
  (the shared QR/barcode scanner). maps goes to Buoy Pinger (the only app with
  a real map UI); location goes to Race Manager (WindFragment); the dead
  play-services-gcm declaration is dropped from the tracking app.
- Race Manager no longer depends on maps: AppPreferences stored a wind
  position as com.google.android.gms.maps.model.LatLng purely as a
  (double,double) holder. Replace it with the existing domain type
  com.sap.sse.common.Position / com.sap.sse.common.impl.DegreePosition.

targetSdk 35 runtime-correctness (local lint checks, not Play Console gates):
- TrackingFragment: register the GPS-disabled receiver with
  RECEIVER_NOT_EXPORTED on API 33+ (guarded native flag; androidx.core 1.0.0
  lacks the ContextCompat overload), suppressing UnspecifiedRegisterReceiverFlag.
- TrackingService: scope the gpsDisabled broadcast to this app's package so it
  reaches the now non-exported receiver (fixes UnsafeImplicitIntentLaunch).
- RaceStateService / RaceLogPollingService: guard setExact with
  canScheduleExactAlarms() on API 31+, falling back to inexact set().
- RaceStateService: declare POST_NOTIFICATIONS and guard the notify() update
  with a runtime permission check on API 33+.

MissingInflatedId false positives: the referenced ids live in qualified layout
variants (e.g. race_schedule_start_time_normal.xml) that lint cannot resolve
from R.layout.race_schedule_start_time; suppress MissingInflatedId on the three
affected onCreateView methods.

Verified: ./gradlew lintDebug assembleRelease (JDK 21) and the CI gate
./gradlew build (JDK 17) both BUILD SUCCESSFUL with 0 fatal lint errors; all
three apps' apk/.version artifacts regenerate.

Assisted-By: Claude Opus 4.8
2026-10-02 19:07:29 +02:00
Axel Uhl 084ce18ee2 Revert "Upgrade Gradle wrapper from 7.6 to 8.14.3 for JDK 21 support"
This reverts commit 700bbcb1d2.
main-202610021650 main-202610021840
2026-10-02 17:56:58 +02:00
Axel Uhl 5a78d3e8e3 Migrate Android build to AGP 8.7.3 for Gradle 8.14.3 / JDK 21
CI dropped JDK 17 for JDK 21; Gradle 7.6 cannot run on JDK 21, so the
wrapper was bumped to 8.14.3 (700bbcb1d2). That alone is insufficient:
AGP 7.2.2 references IncrementalTaskInputs (removed in Gradle 8.0), so
assembleRelease failed. Gradle 8.x and AGP must move together.

Changes:
- Root build.gradle: AGP 7.2.2 -> 8.7.3; grgit 2.3.0 -> 5.3.0 (classpath
  and plugins block); ext.buildTools 30.0.3 -> 34.0.0 (AGP 8.7 floor).
- gradle.properties: enable AndroidX via the Jetifier shim
  (android.useAndroidX=true, android.enableJetifier=true) and set
  android.nonTransitiveRClass=false to restore the merged R so appcompat
  attrs (e.g. R.attr.colorAccent) resolve as before.
- All 4 mobile/ modules: package= in AndroidManifest.xml -> namespace in
  build.gradle; packagingOptions -> packaging; re-enable BuildConfig
  generation (buildFeatures { buildConfig true }), off by default in AGP 8.
- 3 app modules: port the removed applicationVariants APK-copy to the AGP 8
  androidComponents Artifacts API (CopyApkToWww consumes SingleArtifact.APK
  via BuiltArtifactsLoader), reproducing the <packageName>.apk and
  <packageName>.version output in java/com.sap.sailing.www/apps/.
- Jetifier rewrites dependency bytecode but not our own sources, so migrate
  all android.support.* imports/FQNs/XML tags to androidx.* across mobile/.
- AGP 8 makes R fields non-final; convert every switch(R.id...) to if/else
  (18 files) since case labels require constant expressions.

assembleRelease is BUILD SUCCESSFUL under JDK 21 / Gradle 8.14.3 and
produces the same APK and .version artifacts as before. Remaining warnings
are Gradle-10 space-assignment deprecations only; we are intentionally on 8.x.

Assisted-By: Claude Opus 4.8 (claude-opus-5-5)
2026-10-02 17:54:22 +02:00
Axel Uhl 700bbcb1d2 Upgrade Gradle wrapper from 7.6 to 8.14.3 for JDK 21 support
The CI JDK was upgraded from 17 to 21, but Gradle 7.6 only supports
running on JDK up to 19. Running it on JDK 21 made the Groovy script
compiler reject the JDK-21 (class file major version 65) settings.gradle
with 'Unsupported class file major version 65' / 'Could not open settings
generic class cache'. Gradle 8.5 is the first release to support JDK 21;
8.14.3 is the latest 8.x (supports JDK 21-24) and avoids the breaking
changes of the 9.x line. Verified locally: 'gradlew help' configures the
:mobile Android project BUILD SUCCESSFUL under JDK 21.

Assisted-By: Claude Opus 4.8
2026-10-02 16:39:35 +02:00
Axel Uhl 15dbbc03a1 Make TrackedRace.runWhenDoneLoading fire at most once per registration
On ARCHIVE (replay-heavy) startup the restore counters could drift:
numberofracesrestoreddoneloading short and numberofracesstillloading
negative (observed -1). The negative count traced to a single race whose
stillLoading-- callback fired twice against one matching raceAdded +1.

Every restored race arms runWhenDoneLoading twice, from two independent
sites with two distinct runnables (restore path -> doneLoading++, and
RaceAdditionListener.raceAdded -> stillLoading--), each meant to fire
once. The old runWhenDoneLoading removed its listener inside
statusChanged with no one-shot guard. Because notifyListeners snapshots
the listener set under lock and then iterates the snapshot outside the
lock, a removeListener happening during dispatch does not shorten an
already-in-flight iteration. When a race crossed past-LOADING twice
back-to-back under concurrent status pushes (loader finish plus the
stopTracking teardown), a second dispatch snapshotted the listener array
before the first dispatch's removeListener landed, so the callback fired
on both transitions -> two decrements vs one increment -> net -1 (and a
symmetric +1 over-count on doneLoading).

Rewrite runWhenDoneLoading to mirror runWhenPastLoading: a settled
AtomicBoolean CAS makes firing strictly one-shot per registration no
matter how many times or along how many concurrent paths the transition
is delivered, plus a cooperating regatta raceRemoved listener that tears
down and settles-without-firing if the race is removed while still
PREPARED/LOADING/ERROR (closing the companion bug6241 listener-leak
class).

Add TrackedRaceRunWhenDoneLoadingTest as contract tests for the
single-fire / already-done / removed-before-done / fire-then-remove
behaviours. Note these do not reproduce the production concurrency
interleaving: setStatus dispatches statusChanged synchronously and
sequentially, so single-threaded JUnit cannot deterministically recreate
the snapshot/remove race; the fix rests on the settled CAS.

Assisted-By: Claude (Anthropic claude-4.8-opus)
2026-10-02 15:40:14 +02:00
Axel Uhl d1faacfb5b Reapply "trying JDK 21 for Azure mobile app builds"
This reverts commit 35482cad75.
2026-10-02 15:20:31 +02:00
Axel Uhl 35482cad75 Revert "trying JDK 21 for Azure mobile app builds"
This reverts commit 4d924ef358.
2026-10-02 15:18:08 +02:00
Axel Uhl 4d924ef358 trying JDK 21 for Azure mobile app builds 2026-10-02 14:27:28 +02:00
Axel Uhl f5aea106a9 release notes for MPC candidate time comparison fix main-202610011634 main-202610011744 main-202610011805 2026-10-01 17:23:01 +02:00
Axel Uhl 9de68eb70d bug6279: prove candidate time comparator overflow with a regression test
The StartAndEndAwareTimeBasedCandidateComparator compared candidate time
points with (int) (o1.getTimePoint().asMillis() - o2.getTimePoint().asMillis()).
Epoch milliseconds are ~1.7e12, so for candidates more than Integer.MAX_VALUE
ms (~24.8 days) apart the 64-bit difference is truncated to 32 bits and its
sign can flip, turning the comparator into a non-total order. On the "my"
server this pinned all background-executor threads in TreeSet/NavigableSet
navigation and starved the maneuver-calculation queue (live candidate set
span was ~355.9 days, 14.3x the int limit).

CandidateComparatorOverflowTest reproduces the mechanism in isolation against
plain long epoch millis, independent of any domain type:
- truncatingCastReportsACyclicOrder: a single >2^31 ms step overflows and
  flips the sign, producing the cycle T_LOW > T_MID > T_HIGH > T_LOW.
- treeSetNavigationIsConsistentOnlyWithFixedComparator: the broken order makes
  TreeSet first()/last() disagree with the true chronological extremes.
- convergenceLoopTerminatesOnlyWithFixedComparator: an updateCandidates-shaped
  loop over a realistically sized multi-cluster set never drains under the
  buggy comparator (hits a 1,000,000-iteration cap) but drains in ~50 passes
  with Long.compare.

Assisted-By: Claude Opus 4.8 (Claude Code)
2026-10-01 17:09:08 +02:00
Axel Uhl 770bad9fca properly compare candidate time points in mark passing calculator;
the old code subtracted the millisecond representation of the time
points from one another and cast the result to an int which may have
silently led to an overflow in case the difference between the time
points was greater than 49 days. For races with open-ended tracking time
ranges and sporadic fixes along that full time range may have caused
problems here.
2026-10-01 15:59:43 +02:00