wake-alarm/phone_app/lib/services/day_pool_sync.dart
Krzysztof kuhy Rudnicki 34037e55c1 phone: sick days — picker, silent ring, stick stand-down, pre-alarm sync
- Sick days screen (own name, sick icon, cat-2 fill): tap today or a later
  day to mark it sick, tap again to clear; past days inert. Writes the
  sickdays wire format (state sick/cleared + actor, one HLC per write) to
  sickdays-sync/devices via crdt_sync syncLog.
- Mutual exclusion in both pickers: each syncs the other pool on open and
  refuses a date it holds, naming the pool; an unreadable pool refuses all.
- Native SickDays cache (fail closed via DayCache); AlarmReceiver skips the
  ring with reason "sick_day" (no markFired, no ring_started); StickCheck
  verdict sick_day, streak skips sick days; stakes card says so.
- Pre-alarm pool sync: allow-while-idle alarm at 21:00 and A-30 (request
  code 1006) runs a headless engine (poolSyncMain) that pulls both pools
  from RTDB and rewrites both native caches, so a PC mark arrives with the
  app closed. The ring decision stays a cache read.
- JVM unit tests (JUnit 4.13.2) for DayCache, RingDecision,
  PoolSyncSchedule, StickCheck; Dart tests; Dart->Python interop check.

pylint hook skipped: verify.py imports sickdays, not in the hook env until
the sickdays-v0.1.0 tag exists; run by hand in a sickdays venv (10.00/10).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Xfj1sxKH9VBPqpcAni8Jry
2026-10-16 13:22:40 +02:00

77 lines
2.3 KiB
Dart

import 'dart:developer' as developer;
import 'package:crdt_sync/crdt_sync.dart';
/// One pull/merge/push tick for a day pool (free days, sick days), then a
/// refresh of that pool's native ring-path cache.
///
/// Shared by `FreeDaySync` and `SickDaySync`, which differ only in where
/// the log lives and which cache it feeds. Firebase only: neither namespace
/// ever had a GitHub mirror.
///
/// Returns a status line for the screen. Never throws: this also runs
/// unattended (app launch, the pre-alarm sync), where a dead network must
/// degrade to a message. [publish] runs whatever happened, so a day marked
/// offline still reaches the ring path.
Future<String> syncDayPoolLog({
required Future<RemoteStore?> Function() openClient,
required String deviceId,
required String pathPrefix,
required String filename,
required String commitMessage,
required Log Function() snapshot,
required Future<void> Function(Log merged) replaceAll,
required Future<void> Function() publish,
required String logName,
}) async {
final RemoteStore? client;
try {
client = await openClient();
} on Object catch (error, stackTrace) {
developer.log(
'could not open Firebase',
name: logName,
error: error,
stackTrace: stackTrace,
);
await publish();
return 'Error: $error';
}
if (client == null) {
await publish();
return 'Not synced — connect Firebase in Settings.';
}
try {
final merged = await syncLog(
client: client,
deviceId: deviceId,
pathPrefix: pathPrefix,
filename: filename,
localLog: snapshot(),
encode: logToJson,
decode: logFromJson,
commitMessage: commitMessage,
);
await replaceAll(merged);
return 'Synced ✓';
} on Exception catch (e) {
return 'Error: $e';
} finally {
await publish();
}
}
/// Runs [write] (a native-cache write) and swallows a missing platform:
/// widget tests and probe entry points have no channel, the Dart pool is
/// still correct, and the cache is rewritten on the next sync anyway.
Future<void> publishQuietly(
Future<void> Function() write, {
required String logName,
}) async {
try {
await write();
} on Object catch (error) {
developer.log('native cache not written: $error', name: logName);
}
}