Working by area
Five areas of the repo used to have a long-running role thread each. They no longer do: the knowledge lives in the files below, and any short thread reads them before it works. Only the Maintainer stays a long-running thread, because it collects // CLEANUP: markers over time.
A task thread reads the section for each area it touches, does the work itself, and asks the owner (one decision, with a recommended option) when the docs don't settle it. There is one review per PR, not one per area.
Architect: model, engine, schema
Read CONTEXT.md, docs/adr/, docs/architecture.md.
- Name things with the glossary's words. A word the glossary lacks is added to
CONTEXT.mdin the same PR. - A decision that would surprise the next reader gets an ADR in the same PR (next free number, then the sidebar picks it up). Say explicitly when a change contradicts one.
- Before changing a stored shape (database, catalog, files on the phone), check whether existing installs break. If they do, ask the owner whether migration or backward-compatibility code is wanted, and write none until they answer. A schema change is a new SQLDelight migration.
- Logic lives in shared Kotlin with a test. Apps and the server never decide what a workout is.
- Domain text reaches Kotlin through typed generated functions (
DomainText), not string lookups (ADR 0014). A generator that writes files also deletes the stale ones it wrote before. - A change in
build-logicor the shared engine gets its tests run with./gradlew jvmTest jsTest :server:test :tools:i18n:test.
Designer: screens and mockups
Read the design system and ADR 0016.
- A screen starts as an HTML mockup in
design/mockups/drafts/. A separate, later PR moves it toagreed/once the owner has reviewed it on the docs site. Nothing is built from a draft, and a ticket with no agreed mockup waits for one. - Mockups use
var(--cp-*)roles only, so one file works in light and dark. Check both on the docs site before opening the PR. - Android and iOS mirror each other. When they disagree, the agreed mockup wins; when the mockup is wrong, fix it in a PR rather than diverging in code.
- Words never break: text is laid out for the longest language. A tab label has about 12 characters of room; shrink or shorten it before letting a word wrap.
- The warm-up is headed by the builder's sun icon in every mockup and screen.
- The English screen copy follows
docs/brand.md; every string is also added in French (see Translator below). - Theme files are generated from
design/tokens.json: edit the tokens, then runnode design/build-tokens.mjs.
Poulpy keeper: the mascot
Read the brand and the brand decisions.
- Poulpy is never guilt. Pain or rehab always shows the blue side for 72 h; errors are crimson with an icon, never ember.
- Skipping a planned session never counts as missing it; only entries left hanging do. Ember is a comic mood, not a penalty.
- Poulpy speaks in short first-person lines, warm and a little cheeky. He says tu in French while the app says vous.
- A new reward, notification, pose or reaction rule is written into
docs/brand.md(anddocs/brand-decisions.mdwhen it settles an open question) in the same PR. At most one notification a day. - Drawings are generated: edit
design/build-poulpy.mjs, then runnode design/build-poulpy.mjs. - The body map figures are generated too: edit
design/build-body-map.mjsor the outlines indesign/body-map-source/(keep its NOTICE.md), then runnode design/build-body-map.mjs.
Translator: text in every language
Read i18n/README.md and the glossaries in i18n/glossary/.
- A new or edited string goes into
i18n/messages/enandi18n/messages/frin the same PR. Never write a native string file; Android, iOS andDomainTextare generated. - Counts must agree with their number: use an ICU plural for anything countable, so "1 rep" and « 1 tentacule » read right. Check the French forms (
one,many,other) too. - French typography (non-breaking spaces before
: ; ? !and inside « »), and the rest of the checks, are automated byi18n/typography.mjs. - Native speakers proofread in Fink, which opens pull requests. The docs page for them is "Proofread and translate" page of the Translations section; keep it free of repo paths.
- Exercise text lives in
catalog.json. Catalog slugs never change; retire an entry with"retired": trueand raiseversion. - Text that might not fit on a phone (tab labels, buttons, chips) is read in French on a device before the language is called done.
Maintainer: housekeeping (long-running)
The Maintainer owns // CLEANUP: markers, CI (.github/workflows/), gradle/libs.versions.toml, .github/labels.yml, the device-test checklists and triage.
- A change that leaves a workaround, shim, stopgap or known gap marks it
// CLEANUP:with what to remove or fix and why, plus an issue number when there is one, and says so in the PR description. - Android and iOS only build in CI, so a PR is done when CI is green. Anything about Bluetooth, sound, haptics, background running or installs needs the
device-testlabel and a line on what to check on a phone.
Reviews
Each PR gets a single review, not one per area. The docs site workflow comments the preview and the pages to review; nobody posts a docs-review comment of their own. Put what the reviewer must know in the PR description.
Docs on the site
Every guideline, glossary entry, rule or decision is reachable from the published docs site in the PR that writes it: add the file to site/published.mjs and a sidebar entry in site/.vitepress/config.mts. CI fails otherwise.