docs: add CLAUDE.md with the Flutter→Android porting discipline
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) <noreply@anthropic.com>
This commit is contained in:
@@ -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/<path>` — 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`.
|
||||
Reference in New Issue
Block a user