From f62e72b2f2f7b41f00fa421aab34d685b161b376 Mon Sep 17 00:00:00 2001 From: Bryan Van Deusen Date: Thu, 28 May 2026 08:12:23 -0400 Subject: [PATCH] =?UTF-8?q?docs:=20add=20CLAUDE.md=20with=20the=20Flutter?= =?UTF-8?q?=E2=86=92Android=20porting=20discipline?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Encodes the hard rule the operator asked for after repeated divergence: when porting/changing any Android feature that exists in Flutter, read the Flutter source FIRST and replicate it exactly; never silently substitute a different design or scope down — verify the perceived blocker by reading more, and raise it as a question if genuinely blocked. Points at docs/superpowers/parity-map.md as the durable feature→source→target→status reference (gitignored, local). Also captures the standing repo conventions (Forgejo-only, dev→main flow, no in-task builds, detekt gates) so they survive context compaction. Co-Authored-By: Claude Opus 4.7 (1M context) --- CLAUDE.md | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 CLAUDE.md diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 00000000..ea92487b --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,64 @@ +# Minstrel — Claude working instructions + +## Porting discipline (HARD RULE — overrides default behavior) + +The Android native client (`android/`) is a **port of the Flutter client** +(`flutter_client/`). The Flutter client is the source of truth for behavior, +layout, copy, and data flow. When building or fixing any Android feature that +exists in Flutter: + +1. **Read the Flutter source FIRST.** Open the actual `flutter_client/lib/...` + file(s) for the feature before writing any Kotlin. Do not work from memory, + from a summary, or from an assumption about "the shape." If you have not + opened the Flutter file in this session, you have not earned the right to + write the Android version. + +2. **Replicate it exactly.** Match the behavior, the layout structure, the + section order, the empty/loading/error states, the copy strings, the data + sources, and the edge cases. Same number of rows, same fields per row, same + tap targets. + +3. **Never silently substitute a different design.** If the faithful port seems + hard, blocked, or impossible, STOP and verify by reading more of the Flutter + source + the Android data layer. The blocker is usually wrong — the data or + API you think is missing is often already there (e.g. `audio_cache_index` + already carried `lastPlayedAt`; the offline pool was never actually blocked). + If after reading it is genuinely blocked, **raise it as a question** rather + than shipping a scoped-down or alternative design. + +4. **Quote the Flutter file when you start a feature.** Lead the work with + "Here's `flutter_client/lib/` — here's what it does — here's the + Android port," so divergence is caught before code is written, not after. + +5. **Keep the parity map current.** `docs/superpowers/parity-map.md` maps every + feature → Flutter source path → Android target → status. Re-read the relevant + row before porting; update it after. This is the durable reference — rely on + it, not on session memory, because context gets compacted on long sessions. + +## Repo conventions (already in force) + +- **No GitHub — Forgejo only.** PR/issue ops via the forgejo MCP; CI runs under + `.forgejo/workflows/`. Never use `gh` or github.com URLs. +- **Git flow:** work on `dev` → PR to protected `main` → tag release. Never push + to `main`. +- **No in-task tests/builds.** Do not run flutter/gradle/npm `test`/`build`/ + `analyze` during implementation — CI verifies. Codegen scripts only. +- **CI is operator-side.** After `git push origin dev`, the operator reports the + result; don't poll Forgejo. +- **Specs/plans/audits/parity-map live local.** `docs/superpowers/` is + `.gitignored` — save there for operator review; never commit those. +- **detekt gates CI.** Watch the recurring ones: 60-line `LongMethod`, + 11-function-per-file `TooManyFunctions` (use `@file:Suppress` with a one-line + rationale for Compose-helper density), `ReturnCount` ≤ 2, `MagicNumber`, + `MatchingDeclarationName`. Keep lines ≤ 100 chars. + +## Android port shape (quick reference) + +- Screens: `*Screen.kt` with the `@HiltViewModel` inline at the top of the file. +- Repositories: `*/data/*Repository.kt`. Wire models: `models/wire/*Wire.kt`. + Domain models: `models/*.kt`. +- App-lifetime singletons start via the "construct-the-singleton trick" — an + `@Inject lateinit var` in `MinstrelApplication` whose `init {}` wires up. +- Server errors are `{"error":{"code":"...","message":"..."}}`; surface them + through `ErrorCopy.fromThrowable(e)`, never raw `e.message`. +- Cross-device reactivity: collect `EventsStream.events` filtered by `kind`.