Skip to content

Phase 3: persistence + time in app - #5

Merged
hupcus merged 3 commits into
mainfrom
phase-3-persistence
Sep 24, 2026
Merged

hupcus merged 3 commits into
mainfrom
phase-3-persistence

Conversation

@hupcus

@hupcus hupcus commented Sep 24, 2026

Copy link
Copy Markdown
Owner

Phase 3 — Persistence + čas v aplikaci (PLAN.md Phase 3)

Goal: data survives kill, restart, app switches and day changes; at most one flush interval (10 s) lost.

What changed

  • Room v1 (scrollmeter.db, schema JSON in app/schemas/): daily_app_aggregate (SPEC §17 + calibrationVersion + activeScrollMs), scroll_session, daily_app_usage. Insert-or-add in one transaction (INSERT OR IGNORE + additive UPDATE; API 28 has SQLite 3.22, no UPSERT) — ADR-026.
  • ScrollPipeline (pure Kotlin, single-threaded): accumulator + sessions; flush every 10 s while pending / 50th event / new day / onInterrupt / end of the pipeline; NonCancellable write, retry on failure, flushes serialized. Own package never stored.
  • Live totals: unflushed distance is published and added by ScrollRepository → Home "Dnes" is exact immediately.
  • Sleep-safe gaps: session and active-scroll pauses judged on uptime and wall clock (eventTime stops in deep sleep).
  • Time in app (ADR-021, ADR-025): UsageEventsSource → ForegroundTimeAggregator (local days, DST, 2 s activity-switch grace) → UsageSyncer (window from last sync, replace); sync on app resume, stale service connect (≥ 6 h), day change. PACKAGE_USAGE_STATS optional.
  • Settings (DataStore): excluded packages feed the engine and the usage sync; a read error never resets exclusions.
  • Privacy/policy: permission allowlist in PolicyGuardTest and check_manifest_policy.py (incl. uses-permission-sdk-23, exported components); ADR-027 dataExtractionRules — no cloud backup, no device-to-device transfer; pure packages guarded against Android imports.

Definition of Done

  • Robolectric + in-memory Room: same app twice, two apps, day change, repeated flush adds, week/month sums, usage replace + join with aggregates, live unflushed merge
  • Sessions (< 60 s same, > 60 s new, deep sleep ends a session), accumulator (50 events, new day, active time 5 s counts / 6 s not)
  • ForegroundTimeAggregatorTest: two RESUMED = once, 23:50→00:10 = 10 + 10 min, DST 23/25 h, SCREEN_OFF, SHUTDOWN/STARTUP, unpaired PAUSED, open cut at now, split screen, own package, API 28 codes 1/2
  • Syncer with a fake source: window, idempotence, no access, staleness
  • Schema JSON in git
  • Emulator API 34: DB = logcat sum; kill -9 lost only the unflushed ~4 s and the service came back; force-stop documented (it disables the service); screen off/on; two apps; reboot keeps data; time in app = system totalTimeUsed to the second; R8 release runs
  • Real phone: restart on ColorOS (V4), Digital Wellbeing ± 5 % (V5) → handoff.md "Dluh ověření" (Honza's decision: manual tests later)

Gates

./gradlew testDebugUnitTest lintDebug assembleDebug assembleRelease (168 tests, lint: 6 pre-existing debug warnings) · python3 -m unittest discover -s tools -p 'test_*.py' (24) · check_manifest_policy.py ok for debug and release.

Review

  • /topshit over the cumulative diff → sleep-safe gaps, live totals, R8 run, D2D exclusion.
  • Security review (Stage 2): no HIGH; 2 MEDIUM + 3 LOW fixed; 2 INFO → Phase 6 (wipe Room, session retention).
  • Functional review: HIGH (exclusions fail open) fixed; MEDIUM (overlapping flushes) → mutex + test that fails without it; LOW (Home across midnight) fixed.

ADRs: 025, 026, 027. Docs: architecture, measurement-model, accessibility-policy, accuracy-testing, README, handoff.

🤖 Generated with Claude Code

hupcus and others added 3 commits September 24, 2026 08:10
- Room v1 (schema exported): daily_app_aggregate (+ calibrationVersion,
  activeScrollMs), scroll_session, daily_app_usage. Insert-or-add as
  INSERT OR IGNORE + additive UPDATE in one transaction (API 28 has
  SQLite 3.22, no UPSERT); usage days replaced per range.
- ScrollPipeline (pure Kotlin, single-threaded): accumulator, sessions,
  flush every 10 s / 50 events / new day / interrupt / end of pipeline;
  the write is NonCancellable, a failed write is retried. The own package
  is never stored (debug test mode measures it live only).
- Time in app: UsageEventsSource, ForegroundTimeAggregator (local days,
  DST, grace for activity switches), UsageSyncer (window from last sync,
  replace), UsageAccessChecker; sync on resume, on stale connect, on day
  change. PACKAGE_USAGE_STATS, optional.
- SettingsRepository (DataStore) feeds excluded packages to the engine.
- Permissions are an allowlist in PolicyGuardTest and
  check_manifest_policy.py; pure packages guarded against Android imports.
- Home shows today's stored distance.
- ADR-025 (time in app details), ADR-026 (persistence); docs updated.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- Session and active-scroll gaps judge the pause on uptime and wall
  clock: eventTime stops in deep sleep, so an hour asleep between two
  scrolls no longer continues a session.
- The pipeline publishes what it counted but has not committed yet;
  ScrollRepository adds it to every distance, so Home's "Dnes" is exact
  at once instead of up to 10 s late. The confusing since-connect line
  is gone.
- ADR-027: dataExtractionRules exclude every domain from cloud backup
  and device-to-device transfer. Guards: rules file pinned, sdk-23
  permission tag checked, exact package match for the androidx
  permission, exported components need a permission (Compose
  PreviewActivity tolerated in debug only).
- Service and usage sync read settings without falling back to
  defaults on a read error — exclusions never fail open.
- Usage-access intent falls through on any RuntimeException.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
- ScrollPipeline.flush() takes a mutex: Room writes on its own executor,
  which let a ticker / interrupt flush start during a running write.
  New test with a suspending store fails without the mutex.
- Home re-reads today's date at local midnight while it stays open.
- Emulator reboot and the R8 release build recorded in
  docs/accuracy-testing.md; handoff.md: Phase 3 exit report, debt V4/V5,
  open points closed (D2D, calibrationVersion) and added (Phase 6 wipe).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, you can upgrade your account or add credits to your account and enable them for code reviews in your settings.

@hupcus
hupcus merged commit 47c464a into main Sep 24, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant