Skip to content

Fix period logging fragmentation, add merge action to History#166

Merged
mapgie merged 2 commits into
mainfrom
claude/period-logging-corruption-vh3nrx
Jul 3, 2026
Merged

Fix period logging fragmentation, add merge action to History#166
mapgie merged 2 commits into
mainfrom
claude/period-logging-corruption-vh3nrx

Conversation

@mapgie

@mapgie mapgie commented Jul 3, 2026

Copy link
Copy Markdown
Owner

Summary

Root cause: PeriodRepository.periodForDate — the only check that decides whether a "log period" tap should edit an existing period or create a new one — had two gaps:

  • An ongoing period (endDate == null) was treated as extending only through LocalDate.now(). Reopen the app on a later day (or check a future date) and the same ongoing period silently stopped matching, so the next tap created a brand-new, disconnected period instead of continuing it.
  • A period with an explicit end date had no adjacency handling at all. Logging the day right after a closed period (e.g. because the previous day was saved with an explicit end date instead of left "ongoing") fell outside its range entirely and created a new period rather than continuing it. This is exactly how logging June 28 → 29 → 30 fragmented into a 1-day period on the 28th and a separate ongoing period on the 30th, with no way to log or reclaim the 29th.

There was also no way to recover from fragmentation once it happened — History only supports delete, not merge.

Changes

  • PeriodRepository.periodForDate: ongoing periods now match any future date (no upper bound); a period also matches the day immediately after its explicit end date, so consecutive-day logging continues the period instead of splitting it.
  • LogPeriodViewModel: when opened for a date that continues an adjacent period (rather than a date already inside its range), the end date is auto-extended to that date so a single Save commits the continuation.
  • PeriodRepository.mergePeriods (new): merges two period entries into one, combining notes and symptoms, keeping the earlier entry and deleting the later.
  • HistoryViewModel.mergePeriods (new): wraps the repository merge and cleans up the absorbed period's tracking-log entries (flow/symptoms), mirroring the existing delete cleanup.
  • HistoryScreen: each period card now has an overflow menu offering "Merge with <date> period" for its chronological neighbours, with a confirmation dialog before merging.
  • Data migration: PeriodRepository.mergeAdjacentPeriods(), a one-time backfill (same pattern as the existing overlap-merge migration, gated by a new periodAdjacencyMergeDone preference flag) that automatically rejoins periods already fragmented by this bug — any two periods sitting exactly one day apart get merged and the absorbed period's tracking logs cleaned up. Sequenced to run after the existing overlap-merge migration, since resolving an overlap can make a period newly adjacent to a third one. Users who already hit this bug won't need to manually fix their data.
  • Added regression tests for periodForDate covering the ongoing/future-date case and the end-date adjacency case.
  • Added LESSONS.md entry and changelog fragment.

Test plan

  • Log a period for day 1, save with an explicit end date (not "ongoing"), then log the very next day — confirm it opens the same period for edit (extended), not a new one.
  • Log an ongoing period, background/reopen the app on a later simulated date, tap a day within the new "today" — confirm it still edits the same ongoing period.
  • In History, use the overflow menu on two periods with a gap between them and confirm "Merge with…" combines them into one continuous entry, keeping notes/symptoms from both.
  • Seed the DB with two periods one day apart, reinstall/reopen the app, and confirm the migration auto-merges them into one on first launch.
  • Run python3 a11y_check.py (already passing locally).

https://claude.ai/code/session_01QezQYsNLctrCF2mST3Z2Lf

claude added 2 commits July 3, 2026 01:04
periodForDate only matched dates within an existing period's stored range,
treating an ongoing period as extending through today's wall-clock date
rather than indefinitely, and had no adjacency check for the day right
after an explicit end date. Logging consecutive days (especially after
closing a period with an explicit end date instead of leaving it ongoing,
or reopening the app on a later day) could silently create a new,
disconnected period instead of continuing the existing one.

- periodForDate now treats ongoing periods as unbounded, and matches the
  day immediately after an explicit end date so consecutive-day logging
  continues the period instead of fragmenting it.
- LogPeriodViewModel auto-extends the end date to the tapped date when
  continuing an adjacent period, so a single Save commits the extension.
- Added a manual merge action to History (period card overflow menu) so
  already-fragmented periods can be joined back together, combining notes
  and symptoms.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QezQYsNLctrCF2mST3Z2Lf
…close bug

Users who already hit the periodForDate adjacency bug are left with
fragmented periods on disk; the entry-point fix alone doesn't touch
existing data. Adds PeriodRepository.mergeAdjacentPeriods(), a one-time
backfill (same pattern as the existing overlap-merge migration) that
rejoins periods sitting exactly one day apart, then cleans up the
absorbed period's tracking-log entries. Sequenced after the overlap
merge, since resolving an overlap can make a period newly adjacent to
a third one.

Co-Authored-By: Claude <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QezQYsNLctrCF2mST3Z2Lf
@mapgie mapgie marked this pull request as ready for review July 3, 2026 01:49
@mapgie mapgie merged commit 94c57c6 into main Jul 3, 2026
4 checks passed
@mapgie mapgie deleted the claude/period-logging-corruption-vh3nrx branch July 3, 2026 01:50
mapgie pushed a commit that referenced this pull request Jul 3, 2026
Builds on #166's fix (periodForDate day-after-end adjacency, deferred
extension, one-time migration). Two gaps remained against the exact
reported scenario (a period ending day N, an unloggable day N+1, and a
new period starting day N+2):

- periodForDate's day-after-end match only reopens the earlier period
  for a deferred extension; if a second period already starts the
  following day, extending the first just leaves them touching,
  requiring a further manual History merge. PeriodRepository.mergeGapAt
  detects this and merges both immediately when the tapped date bridges
  them, wired through HomeScreen/HomeViewModel.
- mergeAdjacentPeriods (the one-time repair migration) only merged
  already-touching periods (zero-day gap), not periods separated by
  the single unlogged day this bug actually produces. Extended its gap
  check from "touching only" to "touching or one day apart".
- Both the manual History merge and the migration were calling
  deleteLogsForPeriod on the absorbed period — a helper written for
  actual period deletion — silently discarding its flow/symptom
  tracking-log history instead of preserving it. Removed those calls;
  the data is still valid, dated history and isn't orphaned by merging.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WUWr5zTAXGDw1EooCpHxfy
mapgie added a commit that referenced this pull request Jul 5, 2026
…166) (#165)

* Fix fragmented period logging and add History merge action

Logging consecutive days used to fragment into disjoint period entries
once a period's end date became fixed (e.g. via the "no end date set"
confirmation): the next day no longer fell within any existing range,
so tapping it silently started a brand-new, disconnected period with
the in-between day unloggable in either one.

PeriodRepository.resolvePeriodForLogging now checks, before creating a
new entry, whether the logged date is adjacent to an existing period's
start or end and extends it, or bridges a one-day gap between two
periods and merges them. HomeScreen routes calendar taps and the Log
Period FAB through this resolver instead of the old "covers today"-only
check.

The History tab also gains a manual "Merge with previous period" action
per card (via PeriodRepository.mergePeriods, now reusable outside the
one-time migration) so users can fix up any already-fragmented history
that predates this fix or has a gap too large to auto-detect.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WUWr5zTAXGDw1EooCpHxfy

* Bridge the remaining single-day gap and stop merge from deleting data

Builds on #166's fix (periodForDate day-after-end adjacency, deferred
extension, one-time migration). Two gaps remained against the exact
reported scenario (a period ending day N, an unloggable day N+1, and a
new period starting day N+2):

- periodForDate's day-after-end match only reopens the earlier period
  for a deferred extension; if a second period already starts the
  following day, extending the first just leaves them touching,
  requiring a further manual History merge. PeriodRepository.mergeGapAt
  detects this and merges both immediately when the tapped date bridges
  them, wired through HomeScreen/HomeViewModel.
- mergeAdjacentPeriods (the one-time repair migration) only merged
  already-touching periods (zero-day gap), not periods separated by
  the single unlogged day this bug actually produces. Extended its gap
  check from "touching only" to "touching or one day apart".
- Both the manual History merge and the migration were calling
  deleteLogsForPeriod on the absorbed period — a helper written for
  actual period deletion — silently discarding its flow/symptom
  tracking-log history instead of preserving it. Removed those calls;
  the data is still valid, dated history and isn't orphaned by merging.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WUWr5zTAXGDw1EooCpHxfy

---------

Co-authored-by: Claude <noreply@anthropic.com>
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.

2 participants