agentleFS
MicroPyramidMicroPyramid/Django-CRM/mobile/CLAUDE.md

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
  1. Very specific:

    Every request a test sends is checked against the schema (lib/services/api_contract.dart): its path, method and body field names.

    line 39

≈ “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.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
CLAUDE.md

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 in SharedPreferences, logs, crash reports or analytics; never print() 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/ calls ApiService() (lib/services/api_service.dart) with a URL from ApiConfig (lib/config/api_config.dart). Models live in lib/data/models/.
  • API responses are read through the generated wire types in lib/data/api/, never by key. A model's fromJson starts with final w = api.<Component>.fromJson(json); and a provider reads its envelope the same way. Regenerate with dart run tool/api_models.dart after any API change (the enterprise file comes from enterprise-crm's deploy/web-api-types.sh); never edit the .g.dart files. A field missing from a wire class is a backend schema gap: fix the schema.
  • Icons are LucideIcons.*. lib/ names zero Material Icons.*; keep it that way.
  • Style comes from lib/core/theme/theme.dart (AppColors, AppTypography, AppSpacing, AppLayout), whose values follow DESIGN_SYSTEM.md. Cards are flat: a bordered Container, 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 read role directly. 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() and compactMoney() in lib/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, so test/unit/attachment_upload_test.dart pins all four.
  • Forms: lib/widgets/forms/custom_fields_form.dart, unsaved_changes.dart (UnsavedChangesGuard), multi_select_sheet.dart. Shared widgets: the lib/widgets/common/common.dart barrel and badge.dart (StatusBadge, PriorityBadge, LabelPill). @mention chips: MentionText in lib/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.com

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.

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.dart fails 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.dart renders 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 is https://. Without the define, debug builds use http://localhost:8000; on an Android emulator pass http://10.0.2.2:8000 or run adb reverse tcp:8000 tcp:8000. GOOGLE_SERVER_CLIENT_ID overrides the same way.
  • Keep cupertino_icons. Nothing names CupertinoIcons, but SwitchListTile.adaptive needs 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 from file_picker is hidden by Flutter until AGP 9, not fixed.
  • file_picker and flutter_secure_storage upgrade together: file_picker below 12 pins win32 ^5, and flutter_secure_storage 11 needs win32 ^6.
  • PlatformFile is abstract since file_picker 12. Size via length() (null means unmeasurable, which the attachment paths refuse), bytes via readAsBytes(), pick with pickFile(); saveFile() returns Uri?.
  • flutter pub upgrade is not monotonic. A patch release can tighten a constraint and move another package backwards, so read the pubspec.lock diff after upgrading.
  • Crashlytics starts on Android only and collects nothing in debug (lib/main.dart).

Download now (ZIP)

  • In this repositoryOne other file it gives its agents.

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.