Privacy-Period-Tracker/docs/planning/PROJECT_PLAN.md

113 lines
6.4 KiB
Markdown
Raw Normal View History

chore: adopt the project template and add the Kotlin/Compose skeleton Period was a bare directory holding one 2,527-line specification, with no git repository, no tracker and no documentation convention. This is the adoption from Projects/Template/START-HERE-New-Project.md, plus a project that compiles so the hooks and future guards have something real to run against. Documents. scaffold.sh created 19 paths, 0 skipped. The specification moved to docs/planning/PRODUCT_PLAN.md unchanged in substance, with a status header; the capitalised Docs/ is gone. Every scaffolded document was filled in for Period. docs/OPERATIONS.md deleted — an offline app is not a deployed service. DOC_TRUST_MAP.md written last, describing what is actually here, including what this project deliberately does not have. Code. Four Gradle modules. domain/cycle and domain/prediction are kotlin("jvm") and cannot see the Android SDK, so the engine is testable without an emulator — 17 tests pass, 12 of them the acceptance cases from PRODUCT_PLAN.md §51. BaselinePredictionEngine is a robust-median prototype and explicitly not the product; it exists so Batch 02's replacement can be shown to be better rather than merely different. Versions verified against their official sources today rather than inherited from the specification's own numbers, which that document asks for: Kotlin 2.4.10, AGP 9.3.1, Gradle 9.7.0, Compose BOM 2026.08.00, Room 2.8.4, Hilt 2.60.1. AGP 9 ships Kotlin built in, so org.jetbrains.kotlin.android is no longer applied. compileSdk is 37 because current AndroidX requires it; targetSdk stays 36, Play's floor from 2026-08-31, and the difference is deliberate. Six scripts taken into scripts/; the rest declined and named in docs/TOOLS.md. Three hooks in .githooks/, with pre-commit adapted to Gradle. closes #1 closes #2
2026-08-18 02:16:47 -05:00
# Period — Project Plan
```
Status: Current
Owner: _null
Last reviewed: 2026-08-18
Governs: scope, audience, and what this project deliberately is not
Review trigger: Any change of scope, audience, or platform; anything added to or
removed from the "deliberately not" list below
```
> The vision. The **milestones in this repository's issue tracker** hold the
> sequence of work; this holds what the work is *for*.
>
> The full specification — prediction requirements, screen-by-screen UX, copy,
> data model, monetization, compliance — is
> [`PRODUCT_PLAN.md`](PRODUCT_PLAN.md) beside this file. This document is the
> short argument; that one is the detail, and it is not summarised here because
> two copies of one specification disagree the first time either is edited.
## What this is
A private Android period tracker whose one job is to predict a user's next
period well. It learns the individual's cycle from confirmed dates, produces a
most-likely date with an honest window and a confidence label, estimates
ovulation and the fertile window from that, and asks discreetly whether the
period started — using both the *yes* and the *not yet* to improve the next
forecast. Everything core works offline, without an account.
## Who it is for
Someone who wants to know when their period is coming and does not want a
wellness platform. They have probably used a tracker that predicted a generic
28-day cycle at them for a year, and one that put their fertility data somewhere
they could not see. The product's two promises answer exactly those two
experiences: **it learns your cycle**, and **we never sell your data**.
Not "everyone who menstruates". A person who wants community, articles,
pregnancy mode or a symptom encyclopedia is better served elsewhere, and
building for them is what turns this into the app it exists to not be.
## What it is deliberately not
Every "not" here is a decision that stops being re-litigated. From
[`PRODUCT_PLAN.md` §5](PRODUCT_PLAN.md), and it is the most useful list in this
repository:
- not a social network, community or forum
- not a pregnancy tracker — no pregnancy mode in V1
- not an AI chatbot
- not a wellness article feed
- not a diet or exercise tracker
- not a horoscope or lunar-phase app
- not a shop
- not a partner-account or sex-diary product
- not a large mood/symptom library
- not a supplements funnel
- not a diagnostic tool, and **not contraception** — fertility output is an
estimate and says so wherever it appears
- not an app that makes free users worse at the thing it is for
Two of those are harder rules than the rest. **Prediction quality is never a
paid feature**, and **health data never reaches the advertising subsystem**
see [`PRODUCT_PLAN.md` §34](PRODUCT_PLAN.md) and the boundary guard in
[`../architecture/README.md`](../architecture/README.md).
## Stack and platform
Verified against the official sources on 2026-08-18, not inherited from the
specification's own version numbers — which the specification itself asks for.
| Concern | Choice | Why |
| --- | --- | --- |
| Language | Kotlin 2.4.10 | Android is Kotlin-first; null safety and data classes suit a model built out of dates |
| UI | Jetpack Compose, Compose BOM 2026.08.00 | Android is Compose-first; XML layouts are the legacy path |
| Design system | Material 3 (1.4.0) | dynamic colour, dark mode and accessible contrast without hand-rolling any of it |
| Build | Gradle 9.7.0, AGP 9.3.1, Kotlin DSL | version catalog in `gradle/libs.versions.toml` |
| Local structured data | Room 2.8.4 | cycle history is structured, must survive updates, and needs migrations that can be tested |
| Preferences | DataStore 1.2.1 | typed, async, no SharedPreferences main-thread surprises |
| Background work | WorkManager 2.11.2 | reminders must survive process death; **no exact-alarm permission** — a period reminder does not need alarm-clock precision |
| DI | Hilt 2.60.1 | wired at the skeleton stage; retrofitting DI across modules later is the expensive order |
| Prediction engine | pure Kotlin JVM module | testable without an emulator, which is the only way it gets the test coverage §50 asks for |
| Target | `compileSdk`/`targetSdk` 36, `minSdk` 26 | Play requires API 36 for new apps and updates from **2026-08-31** |
## Success looks like
Observable, from [`PRODUCT_PLAN.md` §60](PRODUCT_PLAN.md) — the primary metrics
are about the forecast, not about engagement:
- mean absolute next-period prediction error, falling as confirmed cycles accrue
- share of predictions within ±1 and ±2 days
- measurable accuracy improvement at 3, 6 and 12 confirmed cycles
- a user with a stable 35-day cycle is never predicted at 28
And one qualitative test that decides the rest: **a first-time user enters their
last period and immediately sees a forecast they believe.**
Deliberately not optimised for: time in app, ads viewed, feed engagement. A good
period tracker helps in seconds and gets out of the way.
## Known risks
Written now, while it is still cheap to be honest.
| Risk | What it would look like | What is done about it |
| --- | --- | --- |
| The engine falls back to a global average | a 35-day user predicted at 28 — the specification calls this a core product defect | the acceptance tests in §51 are written before the engine, and run in a module with no Android dependency so they always run |
| Confidence becomes decoration | "High" shown because there is a lot of data rather than because the data agrees | confidence is derived from variability and historical error, and §15's two worked examples are test cases |
| Health data leaks into ad requests | a cycle-state string in an ad extra, or an ads SDK initialised with user properties | a module-boundary guard: `ads` may not depend on the cycle database or the prediction domain, and the guard is proved to fail before it is trusted |
| A notification exposes cycle state on a lock screen | Discreet mode showing the private text publicly | notification privacy modes are a QA pass of their own, tested at every mode |
| Play health-app policy changes before launch | a submission rejected on Data Safety or a medical-claim reading | policy verified immediately before submission, never from memory; the target API deadline is already in the table above |
| A keystore or Play service-account JSON gets committed | a signing key in the history, which deleting the file does not undo | `scripts/secrets.sh` runs before every commit; `local.properties`, `*.jks` and `*.keystore` are ignored from the first commit |