Privacy-Period-Tracker/docs/history/HISTORY.md

99 lines
7.0 KiB
Markdown

# History — Privacy: Period Tracker
```
Status: Current
Owner: _null
Last reviewed: 2026-08-18
Governs: the narrative of how this project got to where it is
Review trigger: A decision reversed, a direction abandoned, or a rewrite
```
## What this is for
The story, in prose: what was tried, what was abandoned, and why. A reader
arriving in six months wants to know which walls have already been walked into,
and that is not something a commit log tells them.
**This is a record of *then*, never a description of *now*.** That distinction is
what makes it safe to leave alone as the project changes — a history document
edited to stay current is not a history, it is a second and competing
description of the present.
If you find yourself updating a sentence here because the code changed, the
sentence belongs somewhere else.
## Where this project started
Period began as a single document. Before any repository existed there was one
2,500-line specification — product, privacy, prediction algorithm, screen copy,
monetization and compliance — written to be handed to a coding agent whole. It
is still here, unchanged in substance, as
[`../planning/PRODUCT_PLAN.md`](../planning/PRODUCT_PLAN.md).
That is worth recording because it explains the shape the first weeks of this
repository had: an unusually complete specification and, at the start, no code at
all. Most projects have the reverse problem.
## Decisions and their reasons
One entry per decision that would otherwise look arbitrary later. The reason
matters more than the decision: "we chose X" ages badly, "we chose X because Y
was true at the time" survives Y stopping being true.
| When | What was decided | Why, at the time |
| --- | --- | --- |
| 2026-08-18 | Kotlin and Jetpack Compose, not Java or XML | Android is Kotlin-first and Compose-first in Google's own current documentation; the alternative was legacy on arrival |
| 2026-08-18 | Room, not files or SharedPreferences, for cycle history | history is structured, must survive app updates, and needs migrations that can be tested — the other options make a migration a hand-written parser |
| 2026-08-18 | The prediction engine is a **pure Kotlin JVM module** | it is the product, so it needs the most tests; a module that cannot see the Android SDK gets tests that run in a second instead of tests that need an emulator |
| 2026-08-18 | A robust personalized statistical model for V1, not a learned model | it is explainable, offline, fast, deterministic and testable, and the product promise is *personal*, not *neural*. A local ML model is a post-V1 option and only if it demonstrably beats this |
| 2026-08-18 | WorkManager for reminders, and **no exact-alarm permission** | a period reminder does not need alarm-clock precision, and requesting that permission is a Play scrutiny cost with no user benefit |
| 2026-08-18 | Ads stay behind an abstraction in a module that cannot reach cycle data | the privacy promise is the product; a rule people remember is not a control, a compile error is |
| 2026-08-18 | One-time purchase to remove ads, not a subscription | it keeps prediction quality free for everyone and avoids an entire class of entitlement defect — nothing to claw back on cancellation |
| 2026-08-18 | Four Gradle modules at the skeleton, not the seventeen the specification sketches | a module created before it has contents is a place for things to be put by accident; the rest arrive with the batch that needs them |
| 2026-08-18 | Eight milestones opened at once, issues filed only under Batch 01 | the roadmap is genuinely known and worth being visible; the *work items* are not, and inventing them would make every tracker percentage permanently wrong |
| 2026-08-18 | OPERATIONS.md deleted rather than kept empty | this is an offline app, not a deployed service; an empty runbook reads as one nobody wrote |
| 2026-08-21 | Spotting is recorded, never predicted | §25 deliberately keeps spotting from resetting or driving the cycle, so predicting it would give it influence the specification withholds. It is also optional and sparsely logged: a forecast built on it would be a pattern invented from whichever cycles she happened to log, presented with the same confidence as one built on periods. The honest shape, if it is ever wanted, is an Insights sentence when her own history strongly supports it — same cycle phase in at least three of the last four cycles — and no calendar marks |
## What was tried and dropped
This is the most useful section in the file and the one most often missing — an
approach abandoned for a good reason will be proposed again by somebody who does
not know it was tried, including you, in a year.
- **Predicting spotting, and the whole feature surface behind it.** Reviewing an
open-source period widget for ideas turned up a symptom library, per-cycle-day
symptom recurrence, hormone curves, water and sleep tracking, a pregnancy mode
and a phase wheel. Almost all of it is the "overloaded women's health super-app"
PRODUCT_PLAN §5 exists to refuse, and its prediction was the naive
last-start-plus-28 §11 forbids — so there was nothing to take from the maths
and little from the features. Three ideas survived as their own batch:
restore-from-export, a year-ahead calendar, and a cycle-length chart. Spotting
prediction did not, and the reason is in the decisions table above.
- **A Room schema-export comparison as the schema-drift guard.** `SchemaTest` was
written believing it caught drift. Room regenerates the export during
compilation, so both sides of every comparison agree by construction, and
adding a column without bumping the version stayed green.
`scripts/schema-guard.sh` asks git instead, which is the one party Room cannot
overwrite.
- **`BaselinePredictionEngine` as the product engine.** A robust median over
recent intervals, correct and not personal enough: PRODUCT_PLAN §11 names a
plain average as an acceptable prototype and an unacceptable final engine.
Replaced in Batch 02 by `PersonalPredictionEngine`, and kept as the control
`EngineComparisonTest` scores the new engine against — which is what makes the
replacement demonstrably better rather than merely different.
- **PRODUCT_PLAN §39's muted teal and sage palette.** Dropped for the owner's
plum-and-rose brand guide, including a pink fertile window where §39 wanted
teal. Safe to drop only because the calendar's states differ in shape rather
than colour, which is what §43 actually requires.
The specification already carries a partial version of it: every `Avoid:` and
`Do not:` list in [`../planning/PRODUCT_PLAN.md`](../planning/PRODUCT_PLAN.md)
is a decision made in advance rather than a lesson learned. When one of them
turns out to be wrong, that reversal belongs here.
## What this file is not
- **Not the plan.** That is [`../planning/PROJECT_PLAN.md`](../planning/PROJECT_PLAN.md).
- **Not the work list.** Open work lives in the tracker as milestones and issues.
- **Not a changelog.** Dated entries go in [`DEVELOPMENT_LOG.md`](DEVELOPMENT_LOG.md).