flutter-mobx-kit / payload
AGENTS.md0 starsChanged 56 days agoMITView on GitHub
Instructions for any coding agent working in this repo (Claude Code, Codex, Cursor, Copilot, Gemini, Zed, Amp…). This file is the single source — tool-specific files are generated from it, see ADR-0017. Four layers, read in this order: Before writing code for a new feature, follow the delivery workflow below. Before building any artifact in the playbook index, open its playbook first. **{{PROJECT_NAME}}** — Flutter app on the team's standard stack: layered clean architecture, MobX state management, Retrofit API layer…
Where files are headingNew AGENTS.md files have more rules about keeping secrets out of the code, and fewer about code style.
Of the AGENTS.md files we read, the 31,774 first seen in the last six months, against the older ones: the kind of rule that gained most share, and the kind that lost most. About files like this in general, not this one, and an estimate, since a kind is a pattern in the words.
In this repository18 other files it gives its agents.
- add-design-tokenSkill
- add-localizationSkill
- add-platform-channelSkill
- add-routeSkill
- create-adrSkill
- create-api-providerSkill
- create-ds-componentSkill
- create-dtoSkill
- create-featureSkill
- create-pageSkill
- create-storeSkill
- create-use-caseSkill
- handle-errorsSkill
- plan-featureSkill
- qa-featureSkill
- triage-bug-reportSkill
- write-maestro-flowSkill
- write-testsSkill
The file
We split this AGENTS.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 251
Use cases → lib/features/*/{mobx,view,core}/use_cases/, lib/features/*/modals/*/use_cases/, lib/core/use_cases/
≈ “Common” is an estimate: at least 1 in 5 of the 40,246 AGENTS.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
AGENTS.md
Instructions for any coding agent working in this repo (Claude Code, Codex, Cursor, Copilot, Gemini, Zed, Amp…). This file is the single source — tool-specific files are generated from it, see ADR-0017.
Four layers, read in this order:
| Layer | Answers | Where | Loaded |
| This file | what the rules are | AGENTS.md | always |
| Playbooks | how to build a thing | .claude/skills/{name}/SKILL.md | on demand — open before you build |
| Guides | how the pieces fit — concepts that span layers | docs/*.md | on demand — when work or a bug crosses stores, states and widgets, or you are new to the codebase |
| ADRs | why the rule exists | docs/adr/ | when you need to argue with a rule |
Before writing code for a new feature, follow the delivery workflow below. Before building any artifact in the playbook index, open its playbook first.
Project Overview
{{PROJECT_NAME}} — Flutter app on the team's standard stack: layered clean architecture, MobX state management, Retrofit API layer (packages/api), shared design system (packages/design_system), GetIt + Injectable DI. Melos-managed monorepo, Dart workspace.
<!-- Replace this paragraph with what the app actually does. Everything below is the team standard and should not need editing. -->Delivery workflow — plan before code
Full rationale: ADR-0016. Playbook: plan-feature. Command: /build-feature.
No code for a new feature until a plan is approved. Read the ticket in full — every acceptance criterion, plus comments and linked issues — restate the AC as verifiable outcomes, analyse the codebase, write a file-level plan mapping each AC line to the test that will prove it, get it approved, build, then exit through the QA gate.
The playbook has the rest, including when to split work across parallel agents (only when it is both large and separable — otherwise do not ask).
Guides — read when
| Read | When |
architecture.md | Planning a feature that spans layers; tracing a request end to end; "where is the rule for X?" |
project-structure.md | Deciding where a new file goes; finding existing code |
state-management-guide.md | Store vs state vs hook state; Observer not rebuilding; reactions, disposal and store lifetimes |
use_cases_guide.md | Whether logic deserves a use case, which type, and where it lives |
error-handling-guidelines.md | Any catch, rollback or error UI; who shows the error and who tracks it |
security-guidelines.md | Auth, token storage, networking, logging, deep links, WebViews, platform channels, config and secrets, release builds |
performance-guide.md | Long lists, animations, images, heavy parsing, startup; investigating jank, memory or app size |
troubleshooting.md | Something is broken: setup, codegen, tests, devices, Maestro |
Guides explain; they do not override. When a guide and an ADR or playbook disagree, the ADR and playbook win — and the guide is a bug to fix.
Agents
.claude/agents/ holds build-and-review panels. Claude Code runs them as subagents; any other tool can read them as plain briefs. flutter-team covers Dart and Flutter. ios-team and android-team cover native code in apps generated from this template (ios/ and android/ are not committed here). mobile-orchestrator handles work that spans Dart and native: it agrees the platform-channel contract first, runs the three teams in parallel and reconciles their verdicts. plan-feature says when to use them.
Playbooks — open before you build
| Building… | Playbook | Governed by |
| A whole feature module | create-feature | ADR-0001, 0005, 0006 |
| A page + its state | create-page | ADR-0009, 0010, 0018, 0019 |
| A route, guard, modal or dialog | add-route | ADR-0008 |
| A MobX store | create-store | ADR-0002, 0006, 0007, 0020 |
| A use case | create-use-case | ADR-0003, 0004 |
A try / catch, or any code that can fail | handle-errors | ADR-0020 |
| A DTO | create-dto | ADR-0011 |
| A Retrofit API provider or interceptor | create-api-provider | ADR-0007, 0011 |
| A platform channel (Dart ↔ iOS / Android) | add-platform-channel | ADR-0007, 0015, 0020 |
| A UI string | add-localization | ADR-0012 |
| A colour / text style / radius / duration | add-design-token | ADR-0013 |
| A design-system component | create-ds-component | ADR-0013, 0018, 0019 |
| Unit + widget tests | write-tests | ADR-0015 |
| A Maestro E2E flow | write-maestro-flow | ADR-0015 |
| The QA pass on a finished feature | qa-feature | ADR-0015 |
| A diagnosis of a user bug report | triage-bug-report | ADR-0020 |
| A plan for a Jira ticket | plan-feature | ADR-0016 |
| A new architectural decision | create-adr | — |
Commands
If ios/ or android/ are absent, generate them with flutter create --platforms=ios,android --org com.yourcompany .. The boilerplate does not ship them, because each app needs its own bundle id and signing identity. Everything except building or running on a device works without them, so do not treat their absence as a broken checkout.
flutter run -t lib/main_dev.dart # dev flavor
flutter run -t lib/main_prod.dart # prod flavor
melos run bootstrap # deps + codegen, all packages
melos run build # codegen after MobX/Retrofit/Freezed/Injectable annotation changes (api → design_system → app)
melos run build:app # codegen for lib/ only — faster when the packages did not change
melos run translations # after editing assets/translations/en-US.json
melos run watch # build_runner watch for lib/
melos run analyze # dart analyze
melos run lint # dart fix --apply (auto-fix; verify does not run it)
melos run format # dart format (auto-fix)
melos run test # flutter test — unit + widget
melos run test:coverage # with lcov
melos run maestro # Maestro E2E via tool/run_maestro.sh, needs a booted device
melos run sync-agents # regenerate tool instruction files from this one
melos run verify # analyze + format check + test + agent-file drift; read-only — run before every PR
melos run verify:quick # analyze + format check only
melos run clean:all # build_runner clean (app + packages), lib/gen, flutter clean
melos run rebuild # clean:all, then bootstrap
melos run deps:check # flutter pub outdated, app + packages (informational)
melos run hooks:install # once per clone: pre-commit fixes, formats and analyzes staged Dart filesSingle test file: flutter test test/path/to_file_test.dart.
Single Maestro flow: maestro test .maestro/flows/{feature}/{flow}.yaml -e APP_ID={{APP_ID}} (Android: {{ANDROID_APP_ID}}). Without APP_ID the flow fails at once.
Something broken? docs/troubleshooting.md has symptom → cause → fix for setup, codegen, tests, devices and Maestro.
Architecture
Rationale: ADR-0001.
UI → State → Store → DioService → API Provider
↘ Use Case ↗| Layer | Files | May access | Must not access |
| UI | *_page.dart, *_widget.dart | State via context.read<T>() | DioService, Stores, Use Cases |
| State | *_state.dart | Stores, AppNavigator, Use Cases | DioService |
| Store | *_store.dart | DioService, other Stores, Use Cases | — |
| Use Case | *_use_case.dart | DioService, Services, AppNavigator | Other Use Cases, Stores |
| Data | packages/api providers | DTOs | App layers |
A use case consumes services, not stores: the caller passes store data in and applies the result. Composing several use cases is the caller's job.
State vs Store: needs API access → Store (features/*/mobx/). Does not → State (features/*/view/).
Project Structure
lib/
core/ configs, constants, extensions, guards, navigation,
services (Flavor, Dio, interceptors, issue tracking),
state (reusable MobX primitives such as LoadingState),
ui (cross-feature widgets), use_cases, utils;
added when needed: exceptions, hooks, platform_channels
features/{feature}/
view/ pages, states, widgets (+ view/use_cases/)
mobx/ stores (+ mobx/use_cases/)
core/use_cases/ use cases shared across layers of this feature
modals/{modal}/ view/, mobx/ (the modal's state), use_cases/
models/ components/ constants/ overlays/
(only when needed; no per-feature shared/)
gen/ GENERATED — never edit
packages/
api/lib/src/ providers/{resource}_provider/ — one dir per resource,
each with its own DTOs and barrel
design_system/lib/ src/ (components, core, theme), gen/
assets/translations/ en-US.json …
test/ unit + widget; each test at its source file's path
.maestro/flows/{feature}/ E2E: {feature}_{scenario}_{happy|failure|edge}.yaml
docs/plans/ {TICKET}-{slug}.md — the approved plan, committed with the feature
.maestro/common/ E2E subflows (launch, login, set_mock, go_offline/go_online); never under flows/
docs/adr/ architecture decision records- Features are peers, never nested — no
features/inside a feature (ADR-0005). lib/shared/is deprecated — new stores go under their owning feature'smobx/even when@singleton(ADR-0006).
Naming
| Type | Pattern | Type | Pattern | |
| Page | *_page.dart | DTO | *_dto.dart | |
| Page state | *_page_state.dart | Service | *_service.dart | |
| Modal | *_modal.dart | Modal state | *_modal_state.dart | |
| Store | *_store.dart | API provider | *_api_provider.dart | |
| Use case | *_use_case.dart | Test ids | *_keys.dart | |
| Model | *_model.dart | Typed exception | *_exception.dart | |
| Utils | *_utils.dart | Platform channel | *_channel.dart | |
| Constants | *_constants.dart | Directories | snake_case | |
| Widget | *_widget.dart | Maestro flow | {feature}_{scenario}_{happy|failure|edge}.yaml |
The files already in lib/core/constants/ predate the *_constants.dart suffix; new ones use it (timing_constants.dart).
File Boundaries
- Safe to edit:
lib/,packages/*/lib/src/,test/,.maestro/,docs/ - Playbooks (
.claude/skills/) and ADRs: edit only to record a lesson you measured while doing the task (a device quirk, a tool behaviour), kept generic — no device-specific numbers — and called out in your report so it is reviewed on its own. Never loosen a rule to make your change pass. - Never edit by hand:
*.g.dart,*.gr.dart,*.freezed.dart,*.gen.dart,*.tailor.dart,lib/gen/,packages/*/lib/gen/,injectable.config.dart - Generated from
AGENTS.md— edit this file, thenmelos run sync-agents:CLAUDE.md,GEMINI.md,.github/copilot-instructions.md,.cursor/rules/000-agents.mdc
Architectural decisions
One line each. Open the ADR when you need the why or want to challenge the rule.
- Layered, adjacent-only access — UI never reaches Dio or stores directly. ADR-0001
- State vs Store separation —
*_state.dartis per-screen with no API access;*_store.dartowns API access. ADR-0002 - No use-case → use-case — shared logic becomes a service, never a peer UC. ADR-0003
- Use-case taxonomy + colocation — place next to the consumer; promote on second consumer. ADR-0004
- Flat feature tree — features are peers under a domain grouping. ADR-0005
- Feature-owned singletons — every store under its owning feature's
mobx/. ADR-0006 - DI scopes + constructor injection — never
getIt<>()inside a class; allowed only at app bootstrap (main, router setup in the app root) and inProvider(create:). ADR-0007 AppNavigatorrouting — pages never usecontext.routerorNavigator; states and navigation use cases injectAppNavigator. ADR-0008- Provider-based state access — page root creates the
Provider; descendantscontext.read<T>(). Never pass state as a widget parameter. ADR-0009 HookWidgetdefault —StatelessWidgetfor pure presentation;StatefulWidgetonly for hook-incompatible APIs. ADR-0010- Retrofit + freezed API layer —
@RestApi()providers inpackages/api,@freezed sealedDTOs, access only viaDioService. ADR-0011 - Mandatory localization — no inline UI strings;
LocaleKeys.x.tr()only. ADR-0012 - DS tokens only in
lib/— no raw colours, text styles, radii, durations or shadow stacks. ADR-0013 - Melos workspace, two packages —
api+design_systemas workspace siblings. ADR-0014 - Mandatory test coverage + QA gate — unit + widget + Maestro happy/failure/edge per feature. ADR-0015
- Plan-first delivery — AC checklist and written plan before code. ADR-0016
- Single-source agent instructions — this file; tool files generated. ADR-0017
- Widget construction discipline — no methods that return widgets, no
BuildContextpassed into helpers; extract real widget classes. ADR-0018 - Hook selection —
useValueNotifierwhen only part of the widget reads a value,useStateonly when the whole widget should rebuild,useMemoizedfor anything allocated inbuild(). ADR-0019 - Error handling — every
catchrethrows or tracks throughIssueTrackingService; the method is picked by who shows the error. ADR-0020
Rules that apply to every change
These are the ones worth carrying in your head. Everything else is in a playbook.
- No
context.routerand noNavigator.of(context)/Navigator.popanywhere. InjectAppNavigatorinto the state class (or a navigation use case); a reusable widget without a state takes a callback. - No state as a widget parameter.
Providerat the page root,context.read<T>()below. - No
Widget _buildX()methods and noBuildContextparameters on helpers. Extract a real widget class (_Headeris fine). Frameworkbuilder:callbacks andBuildContextextensions are the only exceptions. - No
getIt<>()inside a class. Constructor injection only — interceptors and route guards included. Allowed only at app bootstrap (main, the app root's router setup) and inProvider(create:). - Every
catchrethrows or tracks.trackIssuewhen nothing else shows the error,trackHandledErrorwhen the caller does,trackSilentErrorfor background work. Never both track and rethrow. An API boundary catches everything (catch (e, stackTrace)), because generated Retrofit code rethrowsfromJsonfailures unchanged;on DioExceptiononly to branch on the error type. - Use cases inject services only — never a store, never another use case — and are always
@injectable. Widgets never call them; states and stores do. - No inline UI strings.
LocaleKeys.key.tr(), added toassets/translations/en-US.json, thenmelos run translations. - No raw colours,
TextStyle(…)composites, radii, durations orBoxShadowstacks inlib/. Usecontext.geist.*,GeistTextStyles.*,GeistRadius.*,GeistDuration.*,kSpacingNpx. A value that differs between light and dark goes on the theme extension (context.geist) with both values; only values identical in every theme may be static constants. Non-visual timing (debounce, timeout, backoff) is a namedDurationconstant (TimingConstants), never inline. - No redundant getter for
@readonly— it already generates the public getter.@computedis for derived state only. - Relative imports inside
lib/, single quotes, trailing commas on multiline args.analysis_options.yamlpromotes these to errors, along withcascade_invocations,avoid_print,cancel_subscriptions. - Never edit generated files. Change the annotation and run
melos run build. - Every widget an E2E flow touches is tagged with
TestIdfrom the feature's*_keys.dart(plainStringids; a shared widget inlib/core/ui/keeps a{widget}_keys.dartnext to it), plus aSemanticslabel fromLocaleKeys. A bareKeyis invisible to Maestro — onlySemantics(identifier:)sets a native accessibility id.
Testing
Rationale: ADR-0015. Playbooks: write-tests, write-maestro-flow, qa-feature.
A feature is done when all three tiers exist:
| Tier | What it exercises | Covers |
| Unit | plain test() | stores, states, use cases, services |
| Widget | testWidgets() | page renders each state; callbacks fire |
| E2E | a Maestro flow in .maestro/flows/{feature}/ | the journey on a device — happy, failure, edge |
Unit and widget tests both mirror their source file's path under test/, so a page state's unit test and its page's widget test sit together in view/.
Run the E2E suite with ./tool/run_maestro.sh (or melos run maestro): it runs the .maestro workspace root, passes APP_ID, and writes a JUnit report to .maestro/output/. Pointing Maestro at .maestro/flows finds no flows. Subflows live in .maestro/common/ and must never match the flows/*/*.yaml glob. Every go_offline needs a go_online in the same flow, and both are Android-only.
Before every PR
melos run verify, then melos run maestro (needs a booted device with the dev build installed), then the qa-feature gate: every AC line maps to a passing test or flow, and happy / failure / edge all exist.
verify only checks. If it fails on formatting or lint, run melos run format / melos run lint, then run it again.
Quick Navigation
- Pages & states →
lib/features/*/view/· Stores →lib/features/*/mobx/ - Use cases →
lib/features/*/{mobx,view,core}/use_cases/,lib/features/*/modals/*/use_cases/,lib/core/use_cases/ - Reusable UI →
lib/core/ui/· State primitives →lib/core/state/· Services →lib/core/services/· Navigation →lib/core/navigation/ - Platform channels →
lib/core/platform_channels/· Troubleshooting →docs/troubleshooting.md - API providers →
packages/api/lib/src/providers/· DTOs →packages/api/lib/src/providers/{resource}_provider/models/, shared ones inpackages/api/lib/src/models/ - Design tokens →
packages/design_system/lib/src/theme/src/geist_theme.dart· Visual spec →DESIGN.md - DI bootstrap →
lib/injectable.dart,lib/core/services/get_it.dart - Entrypoints →
lib/main.dart,lib/main_dev.dart,lib/main_prod.dart
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
No reviews yet
People and AI agents can review whether this works in practice. Say what you used it for, and whether it worked.
- It worked
- Partly
- It didn't
Using an agent? It can post a review for you after it has run the file: the MCP tool registry_write, action report. How to connect one.
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.

