Commit Graph

10 Commits

Author SHA1 Message Date
6f2ec1fa86 Manual-workout phone sync P4: Dart model + validation
Dart port of the Python manual-workout model so the phone can build the same
signed-on-ingest sync payload the PC form produces.

- lib/models/manual_workout.dart: ManualWorkoutDraft (mirrors the Python
  dataclass), validateManualWorkout (ports validate_manual_workout: required
  fields, HH:MM end-after-start, min duration, RPE range, per-sport rules,
  reflection length), buildEntry/buildSyncPayload (byte-for-byte equivalent of
  the Python build_entry + kind/date), manualSyncRecordId, buildManualRecord.
  The MANUAL_WORKOUT_* constants are inlined here (a const-only file has no
  coverable lines and trips the 100%-coverage file-presence guard), replacing
  models/manual_workout_constants.dart.
- test/models/manual_workout_test.dart: the cross-language wire fixture (an
  identical literal to test_manual_workout.py::TestSyncWireFormat — this catches
  key-name/format drift between the two serializers) plus full validation
  coverage. 29 tests; manual_workout.dart is 100% covered.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PM3auNTifaVgcXWDizZrjH
2026-07-13 12:55:05 +02:00
7722260eeb Fix workout_app service tests on non-Android hosts
Pre-existing failures unrelated to the manual-workout feature, exposed once the
pre-push flutter gate started running the Dart suite:

- http_server_service / sync_service: getExternalStorageDirectory() throws
  UnsupportedError (an Error, not an Exception) on non-Android platforms, so the
  `on Exception` fallbacks didn't catch it and it escaped, failing start() and
  writeWorkoutResult(). External storage is Android-only, so guard the call with
  Platform.isAndroid instead of trying to catch a platform Error.
- sync_service_test: the "updates HttpServerService" assertion expected compact
  JSON ("workout_type":"A") but the payload is pretty-printed
  ("workout_type": "A"); fixed the expectation to match.

Full workout_app suite (191 tests) now passes on this host.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PM3auNTifaVgcXWDizZrjH
2026-07-13 12:47:55 +02:00
3de2928967 Fix workout_app widget tests hanging on sqflite-ffi DB ops
Pre-existing: any DB call (saveSession/getExerciseState/setExerciseWeight) made
directly in a testWidgets body runs in the fake-async zone and hangs sqflite-ffi
for ~3-4 min, then crashes the isolate ("Cannot close sink while adding stream"),
aborting the whole file. This blocked the pre-push flutter gate for any Dart-
touching commit. Two fixes:

- Wrap every in-body DB call in tester.runAsync(() => ...) so it runs on the real
  event loop (setUp already does; the passing tests did their reads inside the
  pump helper's runAsync). history/home/settings screen tests.
- StorageService._open: open the test ':memory:' DB with singleInstance:false so
  each test gets a fresh empty DB. sqflite's default singleInstance returned the
  same cached in-memory DB and resetForTesting never closed it, so rows piled up
  across tests and pushed the asserted widget off-screen ("Found 0 widgets").
  Production keeps the default single shared instance.

188 workout_app tests now pass (was aborting after ~4 min). Unrelated to the
manual-workout feature; needed to unblock its Dart commits.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PM3auNTifaVgcXWDizZrjH
2026-07-13 12:42:42 +02:00
f4d5337f25 Manual-workout phone sync P1: wire format + shared constants + fixture
First phase of syncing phone-logged manual/off-app workouts to the PC so they
count toward the unlock. This phase only establishes the cross-language wire
contract; no behavior change to existing flows.

- _manual_workout.py: build_sync_payload(draft, date) = build_entry fields +
  a `kind="manual_workout"` discriminator + the workout's own `date`; and
  manual_sync_record_id(date, start) -> stable `manual:<date>T<HH:MM>` id.
  A StrongLifts session payload has no `kind`, so an ingesting PC can route on
  it and never mistake a manual for a verified session.
- test_manual_workout.py: TestSyncWireFormat pins the exact literal payload as
  the cross-language contract (the Dart workout_app test will reproduce the
  same literal byte-for-byte; neither side's own round-trip test catches drift
  from the other).
- workout_app/lib/models/manual_workout_constants.dart: hand-maintained mirror
  of the 7 MANUAL_WORKOUT_* constants + the kind value (Python stays source of
  truth; the shared fixture is the drift tripwire).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01PM3auNTifaVgcXWDizZrjH
2026-07-13 10:52:00 +02:00
399f9815c4 Add phone-workout GitHub sync via shared crdt_sync (Workstream C)
Some checks are pending
pre-commit / pre-commit (push) Waiting to run
Tests / test (3.10) (push) Waiting to run
Tests / test (3.11) (push) Waiting to run
Tests / test (3.12) (push) Waiting to run
Sync the workout log across devices using the shared crdt-sync /
crdt_sync_dart CRDT transport (same scheme as diet_guard).

- Python: new _workout_sync.py + _temperature_status_mixin.py; wire sync
  into screen_lock.py / status_view.py; add the crdt-sync git dependency
  and test_sync_fixtures / test_workout_sync suites.
- Dart (workout_app): new workout_sync_service.dart, github_device_auth.dart,
  sync_settings.dart with tests + fakes; settings/workout screens updated.
- docs: workstream-d-shared-crdt-transport.md.

Verification: pytest 747 passed at 100% coverage; flutter analyze clean;
flutter test passed.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017RRCdR4D8SaVrN2xraMQxM
2026-07-11 00:06:59 +02:00
d9119a6582 Add manual (unverified) workout logging with evidence form
Some checks failed
pre-commit / pre-commit (push) Has been cancelled
Tests / test (3.10) (push) Has been cancelled
Tests / test (3.11) (push) Has been cancelled
Tests / test (3.12) (push) Has been cancelled
Adds a rate-limited manual-workout entry point (StatusWindow + locked
retry screen) for activities like table tennis that ADB/RunnerUp can't
verify, gated by a 2/7d + 5/30d budget and a detailed evidence form
rather than free text. Full credit toward the weekly minimum, debt
clearing, and commitment prompt, matching phone/RunnerUp verification.

Also stops tracking personal runtime state (workout_log.json,
sick_history.json, extra_benefits_state.json, scheduled_skips.json,
shutdown_base.json, early_bird_pending.json) in git — these are
regenerated locally and never belong in a public/private repo's
history.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AbPcvAvt1JhiRzEyPDE5dA
2026-07-05 20:08:52 +02:00
63489b07b2 Add comprehensive test suite, backup service, and linting to workout app
- Add sqflite_common_ffi + very_good_analysis; tighten analysis_options
- Add BackupService for JSON export/import of exercise state
- Add full test coverage: models, screens, services, widgets
- Add scripts/check_flutter_coverage.sh to enforce 100% line coverage
- Add docstrings to ExerciseState fields and storage service
- Minor fixes across screens, widgets, and sync/HTTP services

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01VuiPt6GPWkxpLbJFrnfy8U
2026-06-28 08:11:43 +02:00
681bd6f903 feat: overhaul workout app with progress tracking and UX improvements
- Remove automatic rest timer after each set
- Add inline threshold controls (success/fail streaks) on each exercise card
  during an active workout
- Settings: auto-save on change with 600 ms debounce; replace Save button
  with Reset to defaults
- Fix weight display asymmetry in settings (fixed 72 px width, centred)
- Progress screen (renamed from History): per-exercise view shows streak
  counters, weight chart (kg Y-axis, date X-axis, rolling-2 avg for total
  volume), exercise-filtered calendar, and per-exercise session tiles
- Total view shows rolling-2-session average volume chart + full calendar
  + all-session list
- Add WorkoutCalendar widget with monthly navigation
- Store warmupDone in ExerciseResult JSON; surface warmup per session tile

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-06-06 11:39:25 +02:00
c8c0bbe4e4 fix: restore per-rep breaks with audio/vibration
Breaks belong after each rep (circle tap), not removed entirely.
Restored break_banner, audioplayers, vibration, and break_end.mp3 asset.
Break triggers on every circle tap except the last one; 3 min on success,
5 min on failure; sound + vibration fires when the countdown ends.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 16:32:37 +02:00
26d55fb311 feat: add Flutter workout app (StrongLifts replacement)
Full-featured workout tracker with session persistence, auto A/B cycling,
warmup weights (4/5 of target), settings weight stepper, history + progress
graph, HTTP sync server, and crash-safe active session resume.

Removed per-set break timers per user preference. Dropped audioplayers and
vibration dependencies; updated permission_handler to 12.x to eliminate
two of three KGP build warnings (shared_preferences_android is an upstream issue).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-31 16:23:46 +02:00