Privacy-Period-Tracker/docs/DOC_TRUST_MAP.md

199 lines
12 KiB
Markdown
Raw Permalink 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
# Doc Trust Map — which document to believe
```
Status: Current
Owner: _null
Last reviewed: 2026-08-18
Governs: docs/**
Review trigger: Any doc added, deleted or moved; any change to which doc owns a subject
Fires on: added, deleted, moved
```
> Written last, and describing what is **actually here** rather than what the
> template said should be. Its whole value is being accurate about the others.
## Work items are not in this tree
The sequence of work, the open defects, and the things blocking a release all
live in this repository's **issue tracker** at
chore: rename the project to Privacy: Period Tracker "Period" was always a working name — PRODUCT_PLAN.md §55 said so. The real identity arrived with the brand guide and artwork, and this makes the project call itself by it. The repository is renamed in place on Forgejo (null/Period -> null/Privacy-Period-Tracker), which keeps all 30 commits, all 27 issues, all 8 milestones and the four severity labels — verified by counting them on both sides rather than assuming a rename is lossless. The git remote follows in the same breath, so this commit's automatic push is what proves the new URL works. WHAT DELIBERATELY DID NOT MOVE "Period" is the product name AND the central domain word, and a mass rename would have turned the data model into nonsense. PeriodRecord, PeriodWriteResult, confirmPeriodStart, the period_records table and the button that says "Started period" all describe a menstrual period rather than a product, and all 239 references to them are untouched. So are the Kotlin package and applicationId. dev.privacyllc.period already reads correctly under the new name — privacyllc is the company, period is the app — and changing it would rewrite 68 files, rename the Room schema directory and break the hardcoded path in schema-guard.sh for no gain. period.db stays for a sharper reason: renaming a database file orphans the data on every device that already has it. Sixteen files changed, and the diff is small on purpose. THE NAME HAS A SPACE AND THE ARTWORK DOES NOT Canonical is "Privacy: Period Tracker". The supplied wordmark sets it without one, so logo.webp and banner.webp now disagree with every document and with the app itself. BRAND_GUIDE.md §10 is updated to the space form and says plainly that the drawn mark has not caught up. Filed separately rather than papered over, because closing that gap needs an artist and not a rename.
2026-08-18 15:54:34 -05:00
[dream.scheller.ltd/null/Privacy-Period-Tracker](https://dream.scheller.ltd/null/Privacy-Period-Tracker), not in
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
`docs/`. Milestones are batches — eight of them, `Batch 01 — Foundation` through
`Batch 08 — Polish` — issues are deliverables, and severity labels are `P0`,
`P1`, `P2` and `release-blocker`.
This section exists to stop the next contributor starting a fresh markdown
to-do list. A list of things to do in two places is two records that will
disagree, and nothing will say which one is right.
| Question | Answer lives in |
| --- | --- |
| What are we building, and for whom? | [`planning/PROJECT_PLAN.md`](planning/PROJECT_PLAN.md) |
| What exactly does V1 do — prediction rules, screens, copy, compliance? | [`planning/PRODUCT_PLAN.md`](planning/PRODUCT_PLAN.md) |
| What is the sequence of work? | milestones in the tracker |
| What is left in this batch? | open issues under that milestone |
| What is broken right now? | issues labelled `P0` / `P1` / `P2` |
| What makes a release wrong? | issues labelled `release-blocker` |
| What is the next action? | the `nextAction` field on the project at privacyllc.dev — the newest entry in [`history/DEVELOPMENT_LOG.md`](history/DEVELOPMENT_LOG.md) says what it was *then* |
| What is blocking us? | the tracker, for the work; the blockers table at privacyllc.dev, for the stakeholder-facing version |
| What do I do when a piece of work is finished? | [`WORK_CYCLE.md`](WORK_CYCLE.md) |
| What happened, and when? | [`history/DEVELOPMENT_LOG.md`](history/DEVELOPMENT_LOG.md) |
| Why is it built this way, and what was rejected? | [`history/HISTORY.md`](history/HISTORY.md) |
| Did QA pass, and what does the tester think? | [`qa/ClaudeReport.md`](qa/ClaudeReport.md) |
| What did QA actually reach? | [`qa/ClaudeQACoverage.md`](qa/ClaudeQACoverage.md) |
| What is a QA round? | [`qa/ClaudeQAPlan.md`](qa/ClaudeQAPlan.md) |
| How is it built — modules, boundaries, data shapes, migrations? | [`architecture/README.md`](architecture/README.md) |
docs: separate the constants that are physiology from the ones that are tuning PersonalPredictionEngine has thirty constants and no way to tell which kind each is. Both are argued for in comments, in the same voice, so the difference is invisible: MAD_TO_SCALE was tuned against fixtures and can be retuned by anyone with a measurement, while FERTILE_DAYS_BEFORE_- OVULATION is a claim about how long sperm survive and cannot. SCIENCE.md is that line, with a verdict and a citation per constant: supported, needs-a-caveat, contradicted, or not-a-scientific-claim. The last category is listed deliberately, so nobody demands a citation for a tuning parameter and nobody mistakes one for physiology and refuses to tune it. It is a sibling of architecture/README rather than a section in it. That document's subject is the module graph, and it fires its review trigger on a new Gradle module or a changed Room entity; the evidence for a luteal phase changes when somebody publishes. Two subjects with unrelated review cycles in one document means the one nobody is looking at goes stale behind the one they are. One value changed as a result. POPULATION_DEFAULT_DAYS -- the single length assumed for a user with one confirmed period and no interval yet -- moves 28.0 to 29.0. Twenty-eight is the number everyone knows and the large app-data studies agree it is not the average; the mean sits near 29 and only a minority of women average 28. The app's one unavoidable guess is now the population's actual centre rather than folklore's. BaselinePredictionEngine keeps 28.0: it is the frozen control, and moving it would move the yardstick. The luteal phase is filed rather than changed (#56). Fourteen is above the measured mean of 12-13, but moving it shifts what the fertility screen shows every user -- a product decision with its own evidence and QA, not a line inside a calibration change. Also fixes the one user-facing claim that outran its measurements. The Insights banner told a user one cycle in that "a few more and the predictions get noticeably better" -- an accuracy promise made before a single forecast had been scored, and for a woman whose cycles genuinely vary, one the engine can never keep: her forecast gets more honest, not more precise. It now promises the mechanism, which is true by construction and is the standard the rest of that screen already meets. closes #54 closes #55 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-20 16:32:16 -05:00
| Which constants are claims about bodies, and on what evidence? | [`architecture/SCIENCE.md`](architecture/SCIENCE.md) |
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
| What should it feel like, and what words does it use? | [`design/README.md`](design/README.md) |
docs: correct 57 claims the code and tracker disagreed with Every document in the tree audited against the source, the tracker and git history, each finding then given to a second reader tasked with refuting it. 74 raised, 12 refuted, 57 applied. No code changed. THE README DESCRIBED A SKELETON Its Status table — the one place a claim about what is built is allowed to live — still read "there is no usable app yet", with Not built against Room, the four core screens, fertility and notifications, and No round run against QA. Five batches had shipped and three QA rounds had run. TWO DOCUMENTS WERE SILENTLY NEVER FIRING architecture/README.md and design/README.md wrote Governs: as prose ("the Gradle module graph", "the design tokens in core/designsystem"). Neither contains a path token, so doc-triggers.py reduced them to globs matching nothing, and one real glob apiece made them look path-governing rather than subject-governing — the state the script's own header calls invisible. Editing a Room entity never fired the document owning the migration table. Both now fire, proved by running the script. SECURITY.md CLAIMED FOUR UNBUILT PROTECTIONS App lock listed among what works offline; biometric/PIN gating described as protecting app launch; the incognito launcher as existing; Play Billing in the third parties table without the "not yet integrated" marker its neighbours carry. All are Batch 06/07 work. The advertising boundary was overstated in SECURITY.md and the README alike: both said the ads module declares no dependency and a guard proves it. There is no ads module. The pre-declared ":core:ads" to emptySet() rule is stricter than the sentence it replaced and matches nothing until Batch 07, which is why the guard is proved by injection rather than trusted. SMALLER, EACH A REAL TRAP WORK_CYCLE.md pointed at docs/architecture/scripts/forgejo-issue.py, a template path absent here — missed by doc-claims.sh, which reads backticked prose and not fenced blocks. ClaudeReport.md's Round notes said "No rounds yet" after three rounds because ClaudeQAPlan.md's after-a-round list never named that section; the playbook is fixed first. The instrumented-test count was eight in three places and is four. HISTORY.md said the repository had no code and that nothing had been tried and dropped, when three approaches had. DELIBERATELY UNCHANGED ClaudeReport.md's last verified build SHA stays at 0451fbe — no round has run since, and moving it would claim a verification nobody performed. Every DEVELOPMENT_LOG entry stays as written. Guards: ./gradlew check, schema-guard.sh, doc-claims.sh (235 claimed paths, all present), doc-triggers.py, and a link sweep over 21 markdown files. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-18 16:33:14 -05:00
| What colour, logo or type do I use? | [`design/BRAND_GUIDE.md`](design/BRAND_GUIDE.md) — it outranks [`planning/PRODUCT_PLAN.md`](planning/PRODUCT_PLAN.md) §39 where they differ |
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
| What is protected, from whom, and what must never be logged? | [`security/SECURITY.md`](security/SECURITY.md) |
| What is checked before a Play release? | [`security/SECURITY_CHECKLIST.md`](security/SECURITY_CHECKLIST.md) |
| Which script do I run, and can it stop me? | [`TOOLS.md`](TOOLS.md) — the signpost; [`architecture/README.md`](architecture/README.md) has the table |
| What runs before a commit? | [`architecture/githooks/README.md`](architecture/githooks/README.md), and the hooks themselves in `.githooks/` |
| How do I write a check that will actually catch something? | [`architecture/GUARDS.md`](architecture/GUARDS.md) |
**Next action and blockers are recorded at the end of every piece of work, not
when somebody asks.** [`WORK_CYCLE.md`](WORK_CYCLE.md) holds that procedure and
the reason each step is in it.
## The two planning documents, and which owns what
Unusually, this project has two — worth stating plainly, because a reader who
does not know which is which will pick the wrong one.
| | Owns | Length |
| --- | --- | --- |
| [`planning/PRODUCT_PLAN.md`](planning/PRODUCT_PLAN.md) | the **V1 specification**: prediction requirements and their acceptance cases, the data model, every screen and its copy, notification modes, monetization, artwork, Play compliance | ~2,500 lines |
| [`planning/PROJECT_PLAN.md`](planning/PROJECT_PLAN.md) | the **argument**: what this is, who for, what it deliberately is not, the stack and the reason for each choice, what success looks like, known risks | ~110 lines |
`PRODUCT_PLAN.md` is the document this project started as — it existed before
the repository did. Everything else in `docs/` points **into** it rather than
copying out of it, and that is deliberate: a second copy of a fertility
disclaimer or a notification string is how two versions of a promise come to
exist. `architecture/`, `design/` and `qa/` each name the sections of it they
own.
Where the two disagree, `PRODUCT_PLAN.md` is the specification and
`PROJECT_PLAN.md` is the reasoning — resolve it rather than letting both stand.
## Folder layout
| Folder | Contents |
| --- | --- |
| `docs/planning/` | `PRODUCT_PLAN` — the V1 specification. `PROJECT_PLAN` — the vision. Neither is the schedule; that is the tracker. |
| `docs/qa/` | `ClaudeQAPlan` (playbook, passes AH), `ClaudeQACoverage` (what each pass reached), `ClaudeReport` (the verdict) |
docs: separate the constants that are physiology from the ones that are tuning PersonalPredictionEngine has thirty constants and no way to tell which kind each is. Both are argued for in comments, in the same voice, so the difference is invisible: MAD_TO_SCALE was tuned against fixtures and can be retuned by anyone with a measurement, while FERTILE_DAYS_BEFORE_- OVULATION is a claim about how long sperm survive and cannot. SCIENCE.md is that line, with a verdict and a citation per constant: supported, needs-a-caveat, contradicted, or not-a-scientific-claim. The last category is listed deliberately, so nobody demands a citation for a tuning parameter and nobody mistakes one for physiology and refuses to tune it. It is a sibling of architecture/README rather than a section in it. That document's subject is the module graph, and it fires its review trigger on a new Gradle module or a changed Room entity; the evidence for a luteal phase changes when somebody publishes. Two subjects with unrelated review cycles in one document means the one nobody is looking at goes stale behind the one they are. One value changed as a result. POPULATION_DEFAULT_DAYS -- the single length assumed for a user with one confirmed period and no interval yet -- moves 28.0 to 29.0. Twenty-eight is the number everyone knows and the large app-data studies agree it is not the average; the mean sits near 29 and only a minority of women average 28. The app's one unavoidable guess is now the population's actual centre rather than folklore's. BaselinePredictionEngine keeps 28.0: it is the frozen control, and moving it would move the yardstick. The luteal phase is filed rather than changed (#56). Fourteen is above the measured mean of 12-13, but moving it shifts what the fertility screen shows every user -- a product decision with its own evidence and QA, not a line inside a calibration change. Also fixes the one user-facing claim that outran its measurements. The Insights banner told a user one cycle in that "a few more and the predictions get noticeably better" -- an accuracy promise made before a single forecast had been scored, and for a woman whose cycles genuinely vary, one the engine can never keep: her forecast gets more honest, not more precise. It now promises the mechanism, which is true by construction and is the standard the rest of that screen already meets. closes #54 closes #55 Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-20 16:32:16 -05:00
| `docs/architecture/` | modules, boundaries, data shapes, the migration table; `SCIENCE.md` — the physiology constants and their citations; `GUARDS.md`; `githooks/README.md` |
2026-08-20 02:14:08 -05:00
| `docs/design/` | `README.md` — tone, the four rules that settle arguments, and which specification sections own each surface. `BRAND_GUIDE.md` — the owner-supplied visual identity, which outranks `PRODUCT_PLAN.md` §39 on colour. `brand/` — the owner's source art, including the emblem the launcher icon is cut from and the Play listing exports. `dist/` — the onboarding illustration sources, and `splash.webp`, the lockup `docs/data/img/logo.webp` and `banner.webp` are generated from. `screenshots/` — captured app screenshots, for the product page on privacyllc.dev and the Play listing |
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
| `docs/security/` | `SECURITY` — threat model, the advertising boundary, logging rules. `SECURITY_CHECKLIST` — the pre-release list |
| `docs/history/` | `DEVELOPMENT_LOG` (dated, append-only), `HISTORY` (decisions and their reasons), `BATCH_LEDGER` (archived) |
docs: correct 57 claims the code and tracker disagreed with Every document in the tree audited against the source, the tracker and git history, each finding then given to a second reader tasked with refuting it. 74 raised, 12 refuted, 57 applied. No code changed. THE README DESCRIBED A SKELETON Its Status table — the one place a claim about what is built is allowed to live — still read "there is no usable app yet", with Not built against Room, the four core screens, fertility and notifications, and No round run against QA. Five batches had shipped and three QA rounds had run. TWO DOCUMENTS WERE SILENTLY NEVER FIRING architecture/README.md and design/README.md wrote Governs: as prose ("the Gradle module graph", "the design tokens in core/designsystem"). Neither contains a path token, so doc-triggers.py reduced them to globs matching nothing, and one real glob apiece made them look path-governing rather than subject-governing — the state the script's own header calls invisible. Editing a Room entity never fired the document owning the migration table. Both now fire, proved by running the script. SECURITY.md CLAIMED FOUR UNBUILT PROTECTIONS App lock listed among what works offline; biometric/PIN gating described as protecting app launch; the incognito launcher as existing; Play Billing in the third parties table without the "not yet integrated" marker its neighbours carry. All are Batch 06/07 work. The advertising boundary was overstated in SECURITY.md and the README alike: both said the ads module declares no dependency and a guard proves it. There is no ads module. The pre-declared ":core:ads" to emptySet() rule is stricter than the sentence it replaced and matches nothing until Batch 07, which is why the guard is proved by injection rather than trusted. SMALLER, EACH A REAL TRAP WORK_CYCLE.md pointed at docs/architecture/scripts/forgejo-issue.py, a template path absent here — missed by doc-claims.sh, which reads backticked prose and not fenced blocks. ClaudeReport.md's Round notes said "No rounds yet" after three rounds because ClaudeQAPlan.md's after-a-round list never named that section; the playbook is fixed first. The instrumented-test count was eight in three places and is four. HISTORY.md said the repository had no code and that nothing had been tried and dropped, when three approaches had. DELIBERATELY UNCHANGED ClaudeReport.md's last verified build SHA stays at 0451fbe — no round has run since, and moving it would claim a verification nobody performed. Every DEVELOPMENT_LOG entry stays as written. Guards: ./gradlew check, schema-guard.sh, doc-claims.sh (235 claimed paths, all present), doc-triggers.py, and a link sweep over 21 markdown files. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-18 16:33:14 -05:00
| `docs/data/` | `README.md` — the rule, and why this folder cannot be exempted. `img/README.md` — the three required names, their dimensions and the 512 KB ceiling. `img/icon.webp`, `img/logo.webp`, `img/banner.webp` — the marks privacyllc.dev renders |
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
| `docs/` root | this map; `WORK_CYCLE`; `TOOLS` |
`README.md` stays at the repository root; it is the landing page and moving it
breaks that. Everything else lives under `docs/`.
## What this project deliberately does not have
Recorded here because an absence somebody chose and an absence nobody noticed
look identical from outside, and only one of them is fine.
- **docs/OPERATIONS.md — deleted.** Named without backticks, because
`doc-claims.sh` reads a backticked path as a claim the file is there and this
docs: correct 57 claims the code and tracker disagreed with Every document in the tree audited against the source, the tracker and git history, each finding then given to a second reader tasked with refuting it. 74 raised, 12 refuted, 57 applied. No code changed. THE README DESCRIBED A SKELETON Its Status table — the one place a claim about what is built is allowed to live — still read "there is no usable app yet", with Not built against Room, the four core screens, fertility and notifications, and No round run against QA. Five batches had shipped and three QA rounds had run. TWO DOCUMENTS WERE SILENTLY NEVER FIRING architecture/README.md and design/README.md wrote Governs: as prose ("the Gradle module graph", "the design tokens in core/designsystem"). Neither contains a path token, so doc-triggers.py reduced them to globs matching nothing, and one real glob apiece made them look path-governing rather than subject-governing — the state the script's own header calls invisible. Editing a Room entity never fired the document owning the migration table. Both now fire, proved by running the script. SECURITY.md CLAIMED FOUR UNBUILT PROTECTIONS App lock listed among what works offline; biometric/PIN gating described as protecting app launch; the incognito launcher as existing; Play Billing in the third parties table without the "not yet integrated" marker its neighbours carry. All are Batch 06/07 work. The advertising boundary was overstated in SECURITY.md and the README alike: both said the ads module declares no dependency and a guard proves it. There is no ads module. The pre-declared ":core:ads" to emptySet() rule is stricter than the sentence it replaced and matches nothing until Batch 07, which is why the guard is proved by injection rather than trusted. SMALLER, EACH A REAL TRAP WORK_CYCLE.md pointed at docs/architecture/scripts/forgejo-issue.py, a template path absent here — missed by doc-claims.sh, which reads backticked prose and not fenced blocks. ClaudeReport.md's Round notes said "No rounds yet" after three rounds because ClaudeQAPlan.md's after-a-round list never named that section; the playbook is fixed first. The instrumented-test count was eight in three places and is four. HISTORY.md said the repository had no code and that nothing had been tried and dropped, when three approaches had. DELIBERATELY UNCHANGED ClaudeReport.md's last verified build SHA stays at 0451fbe — no round has run since, and moving it would claim a verification nobody performed. Every DEVELOPMENT_LOG entry stays as written. Guards: ./gradlew check, schema-guard.sh, doc-claims.sh (235 claimed paths, all present), doc-triggers.py, and a link sweep over 21 markdown files. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-18 16:33:14 -05:00
one says the opposite. Privacy: Period Tracker is an offline-first Android app
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
distributed through Google Play. There is no host, no container, no uptime and
no restore path of ours. An empty runbook reads as one nobody wrote rather
than one that never applied.
- **docs/planning/FUTURE.md — never created**, and named bare for the same
reason. The Command Center's docs
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
report looks for a batch ledger there and reports it **missing** for every
tracker-first project. That is the expected state; creating the file to turn
the line green would rebuild the second record
[`history/BATCH_LEDGER.md`](history/BATCH_LEDGER.md) was archived for.
- **Most of the template's scripts.** Six were taken; the rest assume a deployed
Node or Postgres service. [`TOOLS.md`](TOOLS.md) names each one and why.
`doc-claims.sh` noting a document that mentions a script this project does not
have is expected, not a failure.
- **A master copy of the hooks under `docs/architecture/githooks/`.** The
docs: correct 57 claims the code and tracker disagreed with Every document in the tree audited against the source, the tracker and git history, each finding then given to a second reader tasked with refuting it. 74 raised, 12 refuted, 57 applied. No code changed. THE README DESCRIBED A SKELETON Its Status table — the one place a claim about what is built is allowed to live — still read "there is no usable app yet", with Not built against Room, the four core screens, fertility and notifications, and No round run against QA. Five batches had shipped and three QA rounds had run. TWO DOCUMENTS WERE SILENTLY NEVER FIRING architecture/README.md and design/README.md wrote Governs: as prose ("the Gradle module graph", "the design tokens in core/designsystem"). Neither contains a path token, so doc-triggers.py reduced them to globs matching nothing, and one real glob apiece made them look path-governing rather than subject-governing — the state the script's own header calls invisible. Editing a Room entity never fired the document owning the migration table. Both now fire, proved by running the script. SECURITY.md CLAIMED FOUR UNBUILT PROTECTIONS App lock listed among what works offline; biometric/PIN gating described as protecting app launch; the incognito launcher as existing; Play Billing in the third parties table without the "not yet integrated" marker its neighbours carry. All are Batch 06/07 work. The advertising boundary was overstated in SECURITY.md and the README alike: both said the ads module declares no dependency and a guard proves it. There is no ads module. The pre-declared ":core:ads" to emptySet() rule is stricter than the sentence it replaced and matches nothing until Batch 07, which is why the guard is proved by injection rather than trusted. SMALLER, EACH A REAL TRAP WORK_CYCLE.md pointed at docs/architecture/scripts/forgejo-issue.py, a template path absent here — missed by doc-claims.sh, which reads backticked prose and not fenced blocks. ClaudeReport.md's Round notes said "No rounds yet" after three rounds because ClaudeQAPlan.md's after-a-round list never named that section; the playbook is fixed first. The instrumented-test count was eight in three places and is four. HISTORY.md said the repository had no code and that nothing had been tried and dropped, when three approaches had. DELIBERATELY UNCHANGED ClaudeReport.md's last verified build SHA stays at 0451fbe — no round has run since, and moving it would claim a verification nobody performed. Every DEVELOPMENT_LOG entry stays as written. Guards: ./gradlew check, schema-guard.sh, doc-claims.sh (235 claimed paths, all present), doc-triggers.py, and a link sweep over 21 markdown files. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-18 16:33:14 -05:00
template keeps one and installs copies; this repository keeps only `.githooks/`,
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
because `pre-commit` here is adapted for Gradle and a second copy would drift.
- **No `Exempt:` declarations.** Every document this convention asks for is
either present in git or listed above as deliberately deleted.
## Source-of-truth ladder
When two sources disagree, believe them in this order:
1. **Verified code behaviour** — read the source, run `./gradlew test`
2. **The issue tracker** — for anything about state of work: what is open, what
closed, when, and by which commit
3. **Specialist docs** — authoritative for their own subject only
4. **History and logs** — a record of *then*, not a description of *now*
A document that describes work as done is describing what was true when it was
written. The tracker is describing now. The `Status:` table in
[`../README.md`](../README.md) is the one place a claim about what is built is
allowed to live, and every row of it cites its evidence.
## The status header
Every document in this tree opens with one, immediately after its H1 — and so
do the documents at the repository root, which `scripts/doc-triggers.py` reads
alongside this tree.
```
Status: Current | Draft | Superseded | Archived
Owner: <who maintains this>
Last reviewed: <YYYY-MM-DD>
Governs: <paths or subject this document is authoritative for>
Review trigger: <the change that should send someone back to this file>
Fires on: <optional added, deleted, moved, changed>
```
**Exactly those four status words.** A checker reads them, and a document whose
status falls outside the list is reported as having an unknown one rather than
being quietly accepted.
**Review trigger** is the line that matters. "Last reviewed" ages on its own and
a reader cannot tell a current document from an abandoned one by looking at it;
a trigger names the event that should bring somebody back. It is also checked: a
header carrying `Status` without `Review trigger` is reported as incomplete —
that combination looks finished and is not.
**Governs** is a comma-separated list, and an entry may explain itself after the
glob with a **spaced dash**`docs/data/** — the assets privacyllc.dev renders`.
`scripts/doc-triggers.py` cuts the entry there and reads the globs from the left.
**Use that form and no other.** A gloss in parentheses, or after a colon, is not
recognised: the whole entry becomes the glob, matches nothing, and the document
is silently never fired — not reported as skipped either, because it still looks
like a path.
**Fires on** is optional and only for the case where `Governs:` is much broader
than the trigger. This file is the extreme of that gap: it governs `docs/**`,
the broadest glob here, while its trigger is one of the narrowest. Omit the line
unless it is genuinely needed — absent means fire on every kind, which is what
almost every document wants.
## Declaring a document deliberately absent
A repository may decide it will not keep one of these documents in git. Say so
**here**, one line per path, anywhere in this file:
```
Exempt: <the path><why, in a few words>
```
**Write the real path only when you mean it.** Outside a fenced block, an
`Exempt:` line naming a real document is not an example — it is a declaration,
and the checker will report that document as deliberately absent. Keep
illustrations fenced, and use placeholders anyway, as the form above does.
docs: correct 57 claims the code and tracker disagreed with Every document in the tree audited against the source, the tracker and git history, each finding then given to a second reader tasked with refuting it. 74 raised, 12 refuted, 57 applied. No code changed. THE README DESCRIBED A SKELETON Its Status table — the one place a claim about what is built is allowed to live — still read "there is no usable app yet", with Not built against Room, the four core screens, fertility and notifications, and No round run against QA. Five batches had shipped and three QA rounds had run. TWO DOCUMENTS WERE SILENTLY NEVER FIRING architecture/README.md and design/README.md wrote Governs: as prose ("the Gradle module graph", "the design tokens in core/designsystem"). Neither contains a path token, so doc-triggers.py reduced them to globs matching nothing, and one real glob apiece made them look path-governing rather than subject-governing — the state the script's own header calls invisible. Editing a Room entity never fired the document owning the migration table. Both now fire, proved by running the script. SECURITY.md CLAIMED FOUR UNBUILT PROTECTIONS App lock listed among what works offline; biometric/PIN gating described as protecting app launch; the incognito launcher as existing; Play Billing in the third parties table without the "not yet integrated" marker its neighbours carry. All are Batch 06/07 work. The advertising boundary was overstated in SECURITY.md and the README alike: both said the ads module declares no dependency and a guard proves it. There is no ads module. The pre-declared ":core:ads" to emptySet() rule is stricter than the sentence it replaced and matches nothing until Batch 07, which is why the guard is proved by injection rather than trusted. SMALLER, EACH A REAL TRAP WORK_CYCLE.md pointed at docs/architecture/scripts/forgejo-issue.py, a template path absent here — missed by doc-claims.sh, which reads backticked prose and not fenced blocks. ClaudeReport.md's Round notes said "No rounds yet" after three rounds because ClaudeQAPlan.md's after-a-round list never named that section; the playbook is fixed first. The instrumented-test count was eight in three places and is four. HISTORY.md said the repository had no code and that nothing had been tried and dropped, when three approaches had. DELIBERATELY UNCHANGED ClaudeReport.md's last verified build SHA stays at 0451fbe — no round has run since, and moving it would claim a verification nobody performed. Every DEVELOPMENT_LOG entry stays as written. Guards: ./gradlew check, schema-guard.sh, doc-claims.sh (235 claimed paths, all present), doc-triggers.py, and a link sweep over 21 markdown files. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-18 16:33:14 -05:00
This repository currently declares none. Where a document is genuinely not applicable it
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
has been **deleted** and recorded above instead, which is the honest form: an
exemption says "kept elsewhere", and `OPERATIONS.md` is not kept anywhere.
**`docs/data/` and `docs/data/img/` cannot be exempted.** A declaration naming
either is refused rather than honoured, because the Command Center renders what
is in them — an exemption would produce a project card with no icon and nothing
docs: correct 57 claims the code and tracker disagreed with Every document in the tree audited against the source, the tracker and git history, each finding then given to a second reader tasked with refuting it. 74 raised, 12 refuted, 57 applied. No code changed. THE README DESCRIBED A SKELETON Its Status table — the one place a claim about what is built is allowed to live — still read "there is no usable app yet", with Not built against Room, the four core screens, fertility and notifications, and No round run against QA. Five batches had shipped and three QA rounds had run. TWO DOCUMENTS WERE SILENTLY NEVER FIRING architecture/README.md and design/README.md wrote Governs: as prose ("the Gradle module graph", "the design tokens in core/designsystem"). Neither contains a path token, so doc-triggers.py reduced them to globs matching nothing, and one real glob apiece made them look path-governing rather than subject-governing — the state the script's own header calls invisible. Editing a Room entity never fired the document owning the migration table. Both now fire, proved by running the script. SECURITY.md CLAIMED FOUR UNBUILT PROTECTIONS App lock listed among what works offline; biometric/PIN gating described as protecting app launch; the incognito launcher as existing; Play Billing in the third parties table without the "not yet integrated" marker its neighbours carry. All are Batch 06/07 work. The advertising boundary was overstated in SECURITY.md and the README alike: both said the ads module declares no dependency and a guard proves it. There is no ads module. The pre-declared ":core:ads" to emptySet() rule is stricter than the sentence it replaced and matches nothing until Batch 07, which is why the guard is proved by injection rather than trusted. SMALLER, EACH A REAL TRAP WORK_CYCLE.md pointed at docs/architecture/scripts/forgejo-issue.py, a template path absent here — missed by doc-claims.sh, which reads backticked prose and not fenced blocks. ClaudeReport.md's Round notes said "No rounds yet" after three rounds because ClaudeQAPlan.md's after-a-round list never named that section; the playbook is fixed first. The instrumented-test count was eight in three places and is four. HISTORY.md said the repository had no code and that nothing had been tried and dropped, when three approaches had. DELIBERATELY UNCHANGED ClaudeReport.md's last verified build SHA stays at 0451fbe — no round has run since, and moving it would claim a verification nobody performed. Every DEVELOPMENT_LOG entry stays as written. Guards: ./gradlew check, schema-guard.sh, doc-claims.sh (235 claimed paths, all present), doc-triggers.py, and a link sweep over 21 markdown files. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-18 16:33:14 -05:00
explaining why. If one of them is ever missing, the mechanism is a filed issue —
[`data/README.md`](data/README.md) says what to file and why a placeholder is
worse than a gap — never an exemption.