170 lines
8.1 KiB
Markdown
170 lines
8.1 KiB
Markdown
|
|
# Period
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
Status: Draft
|
|||
|
|
Owner: _null
|
|||
|
|
Last reviewed: 2026-08-18
|
|||
|
|
Governs: README.md as the project-facing overview for Period
|
|||
|
|
Review trigger: The first buildable feature release; any change to the stack, the
|
|||
|
|
privacy promise, or how the project is built and run
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
A private Android period tracker that learns **your** cycle rather than the
|
|||
|
|
average person's — and never sells your data.
|
|||
|
|
|
|||
|
|
[What it is](#what-it-is) | [Status](#status) | [Build and run](#build-and-run) |
|
|||
|
|
[Repository map](#repository-map) | [Project docs](#project-docs) |
|
|||
|
|
[Agent notes](#agent-notes)
|
|||
|
|
|
|||
|
|
_{ Android / Google Play | Kotlin + Jetpack Compose | Room, offline-first |
|
|||
|
|
Personalized prediction with an honest window | Discreet notifications |
|
|||
|
|
No account, no server, no data sale }_
|
|||
|
|
|
|||
|
|
The core loop is:
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
Track → Learn → Predict → Remind → Learn again
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## What it is
|
|||
|
|
|
|||
|
|
A focused tracker that answers one question well: **when is my next period
|
|||
|
|
likely to start?** It records confirmed period dates, learns the individual's
|
|||
|
|
pattern from them, and produces a most-likely date with a window and a
|
|||
|
|
confidence label — never a bare date presented as fact. From that it estimates
|
|||
|
|
ovulation and the fertile window, and it asks discreetly whether the period
|
|||
|
|
started, using both *yes* and *not yet* to improve the next forecast.
|
|||
|
|
|
|||
|
|
Two promises hold the product up:
|
|||
|
|
|
|||
|
|
> **Your period. Better predicted.** — once there is enough personal history,
|
|||
|
|
> the app must not fall back to a generic 28-day cycle. A user recording 34, 35,
|
|||
|
|
> 36, 34, 35 who is predicted 28 is a core product defect, not a tuning issue.
|
|||
|
|
|
|||
|
|
> **Your cycle belongs to you.** We will never sell period history, fertility
|
|||
|
|
> information, ovulation estimates, cycle predictions or personally identifiable
|
|||
|
|
> information — to advertisers, data brokers or anyone else.
|
|||
|
|
|
|||
|
|
The second is enforced structurally, not remembered: health data cannot reach
|
|||
|
|
the advertising subsystem, because the module that will hold ad code declares no
|
|||
|
|
dependency on the cycle database or the prediction domain, and a guard proves it.
|
|||
|
|
|
|||
|
|
What it deliberately is **not** — no community, no pregnancy mode, no chatbot,
|
|||
|
|
no article feed, no symptom encyclopedia, and not contraception — is in
|
|||
|
|
[docs/planning/PROJECT_PLAN.md](docs/planning/PROJECT_PLAN.md).
|
|||
|
|
|
|||
|
|
## Status
|
|||
|
|
|
|||
|
|
**Skeleton.** There is no usable app yet, and this section says so rather than
|
|||
|
|
listing features that do not exist.
|
|||
|
|
|
|||
|
|
| Surface | Status | Evidence |
|
|||
|
|
| --- | --- | --- |
|
|||
|
|
| Documentation tree and tracker convention | Adopted | this tree; milestones and issues in the tracker |
|
|||
|
|
| Gradle / Kotlin / Compose project | Builds | `./gradlew assembleDebug` and `assembleRelease` both pass |
|
|||
|
|
| Material 3 theme and tokens | Built | `core/designsystem` |
|
|||
|
|
| Four-tab navigation shell | Built, placeholder screens | `app/src/main/kotlin/dev/privacyllc/period/navigation/PeriodApp.kt` |
|
|||
|
|
| Cycle model and interval derivation | Built | `domain/cycle`, 5 tests |
|
|||
|
|
| Prediction engine | **Baseline only** | `domain/prediction`, 12 acceptance tests from PRODUCT_PLAN §51 |
|
|||
|
|
| Room persistence, DataStore | Not built | Batch 01 |
|
|||
|
|
| Onboarding, Today, Calendar, Insights | Not built | Batch 03 |
|
|||
|
|
| Fertility, notifications, privacy features, monetization | Not built | Batches 04–07 |
|
|||
|
|
| QA | No round run | [docs/qa/ClaudeReport.md](docs/qa/ClaudeReport.md) |
|
|||
|
|
|
|||
|
|
`BaselinePredictionEngine` is a robust-median prototype and is **not the
|
|||
|
|
product**. PRODUCT_PLAN §11 names a plain average as an acceptable prototype and
|
|||
|
|
an unacceptable final engine; Batch 02 replaces it with the recency-weighted,
|
|||
|
|
trend-aware, "not yet"-conditioned engine §12 specifies. The acceptance tests
|
|||
|
|
exist now so that replacement can be shown to be better rather than merely
|
|||
|
|
different.
|
|||
|
|
|
|||
|
|
## Build and run
|
|||
|
|
|
|||
|
|
Prerequisites: a JDK 21 and the Android SDK with **platform 37** and
|
|||
|
|
**build-tools 37.0.0** installed, `ANDROID_HOME` set.
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
git config core.hooksPath .githooks # per clone, every clone — see below
|
|||
|
|
|
|||
|
|
./gradlew test # JVM suites, no emulator, ~1s
|
|||
|
|
./gradlew assembleDebug # app/build/outputs/apk/debug/
|
|||
|
|
./gradlew assembleRelease # minified, unsigned
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
`compileSdk` is 37 because the current AndroidX libraries require it;
|
|||
|
|
`targetSdk` is 36, Google Play's floor for new apps from 2026-08-31. They are
|
|||
|
|
deliberately different — raising `targetSdk` opts the app into runtime behaviour
|
|||
|
|
changes and is a tested decision, not a build fix.
|
|||
|
|
|
|||
|
|
**`git config core.hooksPath .githooks` is per clone.** Without it the hooks are
|
|||
|
|
not installed and fail silently, which is the failure mode they exist to
|
|||
|
|
prevent. `post-commit` pushes; see
|
|||
|
|
[docs/architecture/githooks/README.md](docs/architecture/githooks/README.md).
|
|||
|
|
|
|||
|
|
## Repository map
|
|||
|
|
|
|||
|
|
```text
|
|||
|
|
app/ application module, MainActivity, navigation shell
|
|||
|
|
core/designsystem/ Material 3 theme and colour tokens
|
|||
|
|
domain/cycle/ pure Kotlin — period records, cycle derivation
|
|||
|
|
domain/prediction/ pure Kotlin — the forecast, window and confidence
|
|||
|
|
scripts/ the six template scripts this project adopted
|
|||
|
|
.githooks/ pre-commit, commit-msg, post-commit
|
|||
|
|
docs/ product, architecture, design, QA, security, history
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
`domain/*` are `kotlin("jvm")` modules and cannot see the Android SDK. That is
|
|||
|
|
on purpose: the prediction engine is the product, so it needs the most tests,
|
|||
|
|
and tests that need an emulator are tests that do not get run.
|
|||
|
|
|
|||
|
|
## Project docs
|
|||
|
|
|
|||
|
|
Detailed procedures belong in docs; **open work belongs in the tracker**, not in
|
|||
|
|
this README.
|
|||
|
|
|
|||
|
|
| Doc | Purpose |
|
|||
|
|
| --- | --- |
|
|||
|
|
| [docs/DOC_TRUST_MAP.md](docs/DOC_TRUST_MAP.md) | Which document owns which answer, and which source wins when records disagree. **Read this first.** |
|
|||
|
|
| [docs/planning/PRODUCT_PLAN.md](docs/planning/PRODUCT_PLAN.md) | The full V1 specification — prediction requirements, screens, copy, compliance |
|
|||
|
|
| [docs/planning/PROJECT_PLAN.md](docs/planning/PROJECT_PLAN.md) | The short vision, the stack and its reasons, and what this is deliberately not |
|
|||
|
|
| [docs/WORK_CYCLE.md](docs/WORK_CYCLE.md) | What happens at the end of a piece of work |
|
|||
|
|
| [docs/architecture/README.md](docs/architecture/README.md) | Modules, boundaries, data shapes, migrations |
|
|||
|
|
| [docs/design/README.md](docs/design/README.md) | Tone, and the four rules that settle design arguments |
|
|||
|
|
| [docs/security/SECURITY.md](docs/security/SECURITY.md) | Threat model, the advertising boundary, logging rules |
|
|||
|
|
| [docs/TOOLS.md](docs/TOOLS.md) | Which script to run, and which ones can stop you |
|
|||
|
|
|
|||
|
|
## Where the tracker is
|
|||
|
|
|
|||
|
|
Milestones are batches, issues are deliverables, and severity labels are exactly
|
|||
|
|
`P0`, `P1`, `P2` and `release-blocker`. Credentials come from the environment,
|
|||
|
|
never from this repository:
|
|||
|
|
|
|||
|
|
```bash
|
|||
|
|
set -a; . ~/.openclaw/docker-registry.env; set +a
|
|||
|
|
python3 scripts/forgejo-issue.py list
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
Two traps that cost an hour each otherwise: Cloudflare 1010-blocks clients that
|
|||
|
|
do not look like a browser or curl, so every request needs
|
|||
|
|
`User-Agent: curl/8.5.0` — `forgejo-issue.py` sends it and anything new must
|
|||
|
|
too. And `/issues` returns pull requests unless `type=issues` is passed.
|
|||
|
|
|
|||
|
|
## Agent notes
|
|||
|
|
|
|||
|
|
- **Product truth comes from the code and the tracker before prose.**
|
|||
|
|
- Do not keep a work list in this README or anywhere else in `docs/`.
|
|||
|
|
- Finish with [docs/WORK_CYCLE.md](docs/WORK_CYCLE.md), every time: close what
|
|||
|
|
you finished with the evidence, close the milestone if the batch landed,
|
|||
|
|
update the documents the change triggered **in the same commit**, push, log
|
|||
|
|
the entry, then reconcile and write the summary and next action.
|
|||
|
|
- Nothing on privacyllc.dev writes itself except the tracker counts and the
|
|||
|
|
pushed docs.
|
|||
|
|
- **Do not claim a feature is built** unless you can cite the file, test or
|
|||
|
|
screenshot that proves it. The status table above is the standard.
|
|||
|
|
- **Verify current stable versions before changing the toolchain.** The ones in
|
|||
|
|
`gradle/libs.versions.toml` were checked against the official sources on
|
|||
|
|
2026-08-18; PRODUCT_PLAN.md asks for that check rather than for its own
|
|||
|
|
numbers to be trusted.
|
|||
|
|
- Never put a cycle date, a prediction or a fertility state into a log, an
|
|||
|
|
analytics event, or an ad request.
|