agentleFS
NarekManukyanNarekManukyan/flutter-mobx-kit/payload/AGENTS.md

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…

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

    Use cases → lib/features/*/{mobx,view,core}/use_cases/, lib/features/*/modals/*/use_cases/, lib/core/use_cases/

    line 251

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

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

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:

LayerAnswersWhereLoaded
This filewhat the rules areAGENTS.mdalways
Playbookshow to build a thing.claude/skills/{name}/SKILL.mdon demand — open before you build
Guideshow the pieces fit — concepts that span layersdocs/*.mdon demand — when work or a bug crosses stores, states and widgets, or you are new to the codebase
ADRswhy the rule existsdocs/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

ReadWhen
architecture.mdPlanning a feature that spans layers; tracing a request end to end; "where is the rule for X?"
project-structure.mdDeciding where a new file goes; finding existing code
state-management-guide.mdStore vs state vs hook state; Observer not rebuilding; reactions, disposal and store lifetimes
use_cases_guide.mdWhether logic deserves a use case, which type, and where it lives
error-handling-guidelines.mdAny catch, rollback or error UI; who shows the error and who tracks it
security-guidelines.mdAuth, token storage, networking, logging, deep links, WebViews, platform channels, config and secrets, release builds
performance-guide.mdLong lists, animations, images, heavy parsing, startup; investigating jank, memory or app size
troubleshooting.mdSomething 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…PlaybookGoverned by
A whole feature modulecreate-featureADR-0001, 0005, 0006
A page + its statecreate-pageADR-0009, 0010, 0018, 0019
A route, guard, modal or dialogadd-routeADR-0008
A MobX storecreate-storeADR-0002, 0006, 0007, 0020
A use casecreate-use-caseADR-0003, 0004
A try / catch, or any code that can failhandle-errorsADR-0020
A DTOcreate-dtoADR-0011
A Retrofit API provider or interceptorcreate-api-providerADR-0007, 0011
A platform channel (Dart ↔ iOS / Android)add-platform-channelADR-0007, 0015, 0020
A UI stringadd-localizationADR-0012
A colour / text style / radius / durationadd-design-tokenADR-0013
A design-system componentcreate-ds-componentADR-0013, 0018, 0019
Unit + widget testswrite-testsADR-0015
A Maestro E2E flowwrite-maestro-flowADR-0015
The QA pass on a finished featureqa-featureADR-0015
A diagnosis of a user bug reporttriage-bug-reportADR-0020
A plan for a Jira ticketplan-featureADR-0016
A new architectural decisioncreate-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 files

Single 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 ↗
LayerFilesMay accessMust not access
UI*_page.dart, *_widget.dartState via context.read<T>()DioService, Stores, Use Cases
State*_state.dartStores, AppNavigator, Use CasesDioService
Store*_store.dartDioService, other Stores, Use Cases—
Use Case*_use_case.dartDioService, Services, AppNavigatorOther Use Cases, Stores
Datapackages/api providersDTOsApp 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's mobx/ even when @singleton (ADR-0006).

Naming

TypePatternTypePattern
Page*_page.dartDTO*_dto.dart
Page state*_page_state.dartService*_service.dart
Modal*_modal.dartModal state*_modal_state.dart
Store*_store.dartAPI provider*_api_provider.dart
Use case*_use_case.dartTest ids*_keys.dart
Model*_model.dartTyped exception*_exception.dart
Utils*_utils.dartPlatform channel*_channel.dart
Constants*_constants.dartDirectoriessnake_case
Widget*_widget.dartMaestro 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, then melos 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.dart is per-screen with no API access; *_store.dart owns 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 in Provider(create:). ADR-0007
  • AppNavigator routing — pages never use context.router or Navigator; states and navigation use cases inject AppNavigator. ADR-0008
  • Provider-based state access — page root creates the Provider; descendants context.read<T>(). Never pass state as a widget parameter. ADR-0009
  • HookWidget default — StatelessWidget for pure presentation; StatefulWidget only for hook-incompatible APIs. ADR-0010
  • Retrofit + freezed API layer — @RestApi() providers in packages/api, @freezed sealed DTOs, access only via DioService. 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_system as 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 BuildContext passed into helpers; extract real widget classes. ADR-0018
  • Hook selection — useValueNotifier when only part of the widget reads a value, useState only when the whole widget should rebuild, useMemoized for anything allocated in build(). ADR-0019
  • Error handling — every catch rethrows or tracks through IssueTrackingService; 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.router and no Navigator.of(context) / Navigator.pop anywhere. Inject AppNavigator into the state class (or a navigation use case); a reusable widget without a state takes a callback.
  • No state as a widget parameter. Provider at the page root, context.read<T>() below.
  • No Widget _buildX() methods and no BuildContext parameters on helpers. Extract a real widget class (_Header is fine). Framework builder: callbacks and BuildContext extensions 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 in Provider(create:).
  • Every catch rethrows or tracks. trackIssue when nothing else shows the error, trackHandledError when the caller does, trackSilentError for background work. Never both track and rethrow. An API boundary catches everything (catch (e, stackTrace)), because generated Retrofit code rethrows fromJson failures unchanged; on DioException only 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 to assets/translations/en-US.json, then melos run translations.
  • No raw colours, TextStyle(…) composites, radii, durations or BoxShadow stacks in lib/. Use context.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 named Duration constant (TimingConstants), never inline.
  • No redundant getter for @readonly — it already generates the public getter. @computed is for derived state only.
  • Relative imports inside lib/, single quotes, trailing commas on multiline args. analysis_options.yaml promotes these to errors, along with cascade_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 TestId from the feature's *_keys.dart (plain String ids; a shared widget in lib/core/ui/ keeps a {widget}_keys.dart next to it), plus a Semantics label from LocaleKeys. A bare Key is invisible to Maestro — only Semantics(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:

TierWhat it exercisesCovers
Unitplain test()stores, states, use cases, services
WidgettestWidgets()page renders each state; callbacks fire
E2Ea 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 in packages/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

Download now (ZIP)

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
What did you use it for? e.g. Reviewing pull requests in a Rails app

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.