Skip to content

Restructure period logging around per-day entries - #172

Merged
mapgie merged 3 commits into
mainfrom
claude/period-logging-restructure-gujoq7
Aug 22, 2026
Merged

Restructure period logging around per-day entries#172
mapgie merged 3 commits into
mainfrom
claude/period-logging-restructure-gujoq7

Conversation

@mapgie

@mapgie mapgie commented Aug 22, 2026

Copy link
Copy Markdown
Owner

Why

Periods were bundled into one record per episode: a single start/end range with one flow level and one symptom set. That doesn't reflect how periods work: day 1 and day 4 of the same period have different flow, different symptoms, different everything. It also bred a family of boundary bugs (fragmented entries, eternally "ongoing" periods, extend-vs-create races) that previous fixes had to patch one edge at a time.

What changed

The day is now the unit of period logging.

Data model

  • New period_days table: one row per day the user marks as a period day (unique dates). DB migration v22→23 expands existing episode ranges into day rows (ongoing entries are capped at 10 days so a stale open entry doesn't fabricate months of data).
  • Period episodes (the existing periods table) are now derived from those days: days that are consecutive, or within a configurable gap tolerance of each other, form one period. The rebuild preserves row ids and notes via span-overlap matching, so History, Stats, widgets, reminders, and export all keep reading the same schema.
  • Day-specific values (flow, symptoms, pinned categories) live in the per-day tracking logs keyed to each date, replacing the old behaviour where the Log Period screen only ever wrote to the start date.

Behaviour

  • Day 1: logging a day starts a period (its flow/symptoms recorded for that day).
  • Day 2..N: each subsequent logged day extends the period and carries its own independent values. A day within the gap tolerance (default 1 unlogged day, configurable 0-3 in Settings → Cycle) still counts as the same period; logging a day that sits between two fragments bridges and merges them.
  • Ending: a period with no explicit end is deemed ended once the tolerance window passes with no period day logged. This reconcile runs on app start and in the daily worker, so periods close even if the app stays shut. An explicit "until" date can also be set, including past the last individually logged day.
  • Editing: tapping any calendar day in or near a period opens that day for logging. The editor shows "day N of this period", can move the episode's start/end, remove a single day from the period, or delete the whole episode.
  • Every other category remains individually loggable for any date (existing per-day log screens), or alongside a period day via the pinned "log with period" categories, now saved against the selected day.

Consequential cleanups

  • Replaced the one-time overlap/adjacency merge fixups with the episode reconcile, which repairs fragmented or overlapping legacy entries on every start.
  • History merge now materialises the gap days (each day keeps its own logged values) instead of unioning symptom sets; delete/undo round-trip the day rows; deleting a period cleans per-day logs across its whole span rather than only the start date.
  • Exports gain per-day data (days array in JSON, logged_days CSV column); import restores it and still accepts legacy exports without it.

Tests

PeriodRepositoryTest rewritten around the new model: episode derivation (start/continue/gap/bridge/backfill), auto-end via reconcile, explicit end dates surviving rebuilds and being dropped when superseded, day removal (shrink/split/delete), delete/restore/merge, import/export round-trips including legacy formats, and tolerance-aware periodForDate boundaries.

🤖 Generated with Claude Code

https://claude.ai/code/session_015ZijqU65RHMEqCnz8TjHQp


Generated by Claude Code

claude added 3 commits August 22, 2026 10:09
Periods were stored as a single record per episode (start, end, one flow
level, one symptom set), which cannot represent how periods actually
unfold. This makes the individual day the unit of logging:

- New period_days table (one row per logged day, unique dates) is the
  source of truth; DB migration v23 expands existing episode ranges into
  day rows (ongoing entries capped at 10 days).
- Period episodes are now derived by grouping days that fall within a
  user-configurable gap tolerance (0-3 days, default 1, new Cycle
  setting). The derived rows keep the existing periods table, ids, and
  notes, so stats, widgets, reminders, and history keep working
  unchanged.
- Logging a day starts a period, continues one, extends it backwards, or
  bridges two fragments, all through the same operation; each day
  carries its own flow, symptoms, and pinned-category values keyed to
  that date in the tracking logs.
- A period with no explicit end is deemed ended once the tolerance
  window passes without a new period day (reconciled on app start and by
  the daily worker); an explicit "until" date can also close it,
  including past the last individually logged day.
- Log Period screen is day-centric: it shows which period the day
  belongs to (day N), loads and saves that day's values, can set or
  clear the explicit end date, and can remove a single day from a
  period.
- History merge now materialises the gap days; delete/undo carry the
  day rows; deleting a period cleans per-day logs across its whole span.
- Exports gain per-day data (JSON "days" array, CSV logged_days column);
  import restores it and still accepts legacy exports.
- Replaced the one-time overlap/adjacency merge fixups with the
  reconcile pass, which repairs fragmented or overlapping episodes on
  every start.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015ZijqU65RHMEqCnz8TjHQp
The new export/import round-trip tests exercise JSONObject/JSONArray,
which are unmocked android.jar stubs in local unit tests: exportData
threw and importData surfaced Failure, failing 3 tests in CI. Adding the
real org.json artifact to the test classpath (it precedes the mockable
android.jar) lets those paths run for real.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015ZijqU65RHMEqCnz8TjHQp
The licenses CI check requires every libs.versions.toml change to be
reflected in LicensesScreen.kt, but a test-only dependency is never
shipped and doesn't belong on the attribution screen. Declaring it
inline in the test configuration keeps the catalog and the licenses
screen in sync.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015ZijqU65RHMEqCnz8TjHQp
@mapgie
mapgie marked this pull request as ready for review August 22, 2026 16:20
@mapgie
mapgie merged commit bbc31b3 into main Aug 22, 2026
4 checks passed
@mapgie
mapgie deleted the claude/period-logging-restructure-gujoq7 branch August 22, 2026 16:20
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