Django-CRM / mobile
CLAUDE.md2.5k starsChanged 5 days agoMITView on GitHub
Flutter client for the BottleCRM API: Riverpod 3 (hand-written AsyncNotifier and Notifier providers, no codegen), go_router, Material 3 and Lucide icons. ../CLAUDE.md applies here as well (security, web and mobile parity, comments, em dashes); this file covers only what is specific to the app. No CI runs any of these. ../.github/workflows/ covers the backend, frontend and docs only, so local flutter analyze and flutter test are the whole gate. Run both before calling a change done.
We split this CLAUDE.md into the rules it gives an agent
1 rule. About 0 are common, 0 are distinctive, and 1 is very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈0
Distinctive · estimate
This author's own choices. Worth a look.
1
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 39
Every request a test sends is checked against the schema (lib/services/api_contract.dart): its path, method and body field names.
≈ “Common” is an estimate: at least 1 in 5 of the 25,680 CLAUDE.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
CLAUDE.md
Flutter client for the BottleCRM API: Riverpod 3 (hand-written AsyncNotifier and Notifier providers, no codegen), go_router, Material 3 and Lucide icons. ../CLAUDE.md applies here as well (security, web and mobile parity, comments, em dashes); this file covers only what is specific to the app.
House rules
- The app is not a security boundary. Any rule enforced here (required fields, role-based hiding, ownership) must also exist in the DRF serializer or view. If the backend does not enforce it, say so.
- Tokens live only in
lib/services/token_storage.dart(Keystore/Keychain). Never inSharedPreferences, logs, crash reports or analytics; neverprint()a JWT, refresh token or PII. Never disable TLS validation, even behind a debug flag. - Screens do not call the network. A provider in
lib/providers/callsApiService()(lib/services/api_service.dart) with a URL fromApiConfig(lib/config/api_config.dart). Models live inlib/data/models/. - API responses are read through the generated wire types in
lib/data/api/, never by key. A model'sfromJsonstarts withfinal w = api.<Component>.fromJson(json);and a provider reads its envelope the same way. Regenerate withdart run tool/api_models.dartafter any API change (the enterprise file comes from enterprise-crm'sdeploy/web-api-types.sh); never edit the.g.dartfiles. A field missing from a wire class is a backend schema gap: fix the schema. - Icons are
LucideIcons.*.lib/names zero MaterialIcons.*; keep it that way. - Style comes from
lib/core/theme/theme.dart(AppColors,AppTypography,AppSpacing,AppLayout), whose values followDESIGN_SYSTEM.md. Cards are flat: a borderedContainer, no elevation or shadow. - Mobile first is the baseline, so check the other direction: a new screen at tablet width and with the system font scaled up, with tap targets of about 44px or more.
Reuse before writing
- Admin gate:
isOrgAdminProvider(lib/providers/auth_provider.dart). Never readroledirectly. Ownership:myEmailProvider. - "Today" is the org's, not the device's:
lib/services/org_date.dart(orgToday,todayIn,orgDaysUntil,orgTimeZoneOf). - Currency:
Currency.symbolFor(code)(lib/data/models/deal.dart), which never falls back to$. Invoice amounts:money()andcompactMoney()inlib/screens/invoices/invoice_format.dart. - Attachments:
lib/services/attachment_upload.dart(AttachmentTarget,downloadAttachment) and nowhere else. Each record type posts its own multipart field (task_attachment,lead_attachment,opportunity_attachment,case_attachment). A wrong name is answered 200 with nothing stored, sotest/unit/attachment_upload_test.dartpins all four. - Forms:
lib/widgets/forms/custom_fields_form.dart,unsaved_changes.dart(UnsavedChangesGuard),multi_select_sheet.dart. Shared widgets: thelib/widgets/common/common.dartbarrel andbadge.dart(StatusBadge,PriorityBadge,LabelPill). @mention chips:MentionTextinlib/widgets/common/mention_text.dart.
Commands
flutter pub get
flutter run --dart-define=API_BASE_URL=http://<host>:8000 # debug allows http
flutter test
flutter test test/unit/attachment_upload_test.dart # one file
flutter analyze --no-fatal-infos
dart format .
flutter build appbundle --release --dart-define=API_BASE_URL=https://crm.example.comNo CI runs any of these. ../.github/workflows/ covers the backend, frontend and docs only, so local flutter analyze and flutter test are the whole gate. Run both before calling a change done.
Tests
- Every request a test sends is checked against the schema (
lib/services/api_contract.dart): its path, method and body field names.test/flutter_test_config.dartfails the test otherwise. A failure means the request is wrong, or the fixture is; never loosen the check. - Layout:
test/unit/,test/screens/<feature>/,test/widgets/. Fake file picks:test/helpers/fake_platform_file.dart. JWTs carrying an org timezone:test/support/org_zone.dart. test/screens/phone_viewport_test.dartrenders screens at 390x844 at two text scales (usePhone()), asserting no overflow. Add a case for every new screen: it catches overflows that review misses.
Gotchas
- The API base URL is compile-time (
--dart-define=API_BASE_URL), on purpose: a runtime setting would let anything that can write app storage redirect every JWT. A release build throws unless it ishttps://. Without the define, debug builds usehttp://localhost:8000; on an Android emulator passhttp://10.0.2.2:8000or runadb reverse tcp:8000 tcp:8000.GOOGLE_SERVER_CLIENT_IDoverrides the same way. - Keep
cupertino_icons. Nothing namesCupertinoIcons, butSwitchListTile.adaptiveneeds the font and the build fails without it. - Do not set
android.builtInKotlin=true. AGP 8.x hardcodes built-in Kotlin to 2.0.0, a downgrade from our 2.4.10 and below Flutter's 2.2.20 floor. The Kotlin Gradle Plugin warning fromfile_pickeris hidden by Flutter until AGP 9, not fixed. file_pickerandflutter_secure_storageupgrade together: file_picker below 12 pinswin32 ^5, and flutter_secure_storage 11 needswin32 ^6.PlatformFileis abstract since file_picker 12. Size vialength()(null means unmeasurable, which the attachment paths refuse), bytes viareadAsBytes(), pick withpickFile();saveFile()returnsUri?.flutter pub upgradeis not monotonic. A patch release can tighten a constraint and move another package backwards, so read thepubspec.lockdiff after upgrading.- Crashlytics starts on Android only and collects nothing in debug (
lib/main.dart).
In this repositoryOne other file it gives its agents.
- CLAUDE.mdCLAUDE.md
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
Reviews can't be read right now.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

