All jobs

Review Pull Requests on a Flutter App

Yes. An agent checks out the pull request, runs flutter analyze and flutter test in its sandbox, and posts one review on rebuilds, async gaps and state.

Run this on RushIn a Project that has your repository.

What the agent does

The agent checks out the pull request's branch and reads what the app expects of its toolchain before building. In an app shaped like simplezhli/flutter_deer, pubspec.yaml names flutter_deer 1.3.3+22 with sdk: ">=3.4.0 <4.0.0" and flutter: ">=3.19.0", provider ^6.1.2 for state, fluro ^2.0.5 for routes, dio ^5.9.0 for the network layer, rxdart, cached_network_image, webview_flutter and a git dependency on flutter_2d_amap, with flutter_test, flutter_driver, integration_test and test under dev_dependencies and generate: true so flutter pub get writes DeerLocalizations from lib/l10n as l10n.yaml describes. .github/workflows/flutter-drive.yml pins Flutter 3.47.4 and runs only flutter drive --target=test_driver/driver.dart on an iOS simulator; there is no workflow that runs flutter analyze or flutter test, so the review is the first place those two commands run against the change.

It runs flutter pub get, then flutter analyze. analysis_options.yaml turns on strict-casts, strict-inference and strict-raw-types, a long explicit lint list that includes always_declare_return_types and annotate_overrides, a formatter page_width of 100, and excludes lib/l10n/**, lib/generated/json/**, lib/widgets/bezier_chart/** and test_driver/**; a diff that adds a file under lib/ picks up every one of those rules. Then flutter test. The suite is small and specific: test/widget_test.dart pumps MessagePage inside a MaterialApp and expects the text 消息; the eight *_accessibility_test.dart files under test/ wrap each module's pages in MyApp(home: page, theme: ThemeProvider().getTheme()) and assert meetsGuideline(labeledTapTargetGuideline), collected by test/accessibility_test.dart; and test/net/dio_test.dart calls https://api.github.com/ for real through DioUtils, which fails without network and is reported as such rather than counted as a regression.

The review then reads the diff against the patterns the app already uses, because a Flutter finding is usually about where a rebuild or an await lands. Widget rebuilds: list pages subclass BaseListProvider<T> from lib/order/provider/base_list_provider.dart, which calls notifyListeners() on every add, addAll, insert, remove and clear, so a loop that adds items one at a time, or a Consumer placed above a whole page instead of a Selector around the part that changes, rebuilds far more than intended. Async gaps with BuildContext: presenters such as lib/order/presenter/order_search_presenter.dart run requestNetwork and touch view.provider in onSuccess, and the ChangeNotifierMixin on _LoginPageState ties notifier lifetimes to the widget; any await followed by a Navigator, Toast or context use without a mounted check is a finding with its line. State: hasMore on the list provider is set from data.items!.length == 30 in the presenter, so a change to page size that misses one of the two sides is called out.

Where the diff touches lib/net/, the agent reads lib/net/dio_utils.dart and lib/net/error_handle.dart alongside it: DioUtils sets ResponseType.plain, accepts every status with validateStatus: (_) => true and leaves status handling to AdapterInterceptor, and ExceptionHandle.handleException maps a DioException to codes 1000 to 1007 and 9999 from _errorMap. A new interceptor that throws, or a new code with no map entry, is a null-check crash at runtime and a blocking finding. The review separates blocking findings from suggestions, quotes the flutter analyze and flutter test output and the Flutter version used, and pushes nothing to the branch.

Start it with one API call

POST https://api.prix.dev/o/{org}/sessions with this body:

{
  "agent": "claude",
  "input": "Review the open pull request on this Flutter app. Check out its branch, read pubspec.yaml, analysis_options.yaml and the workflow under .github/workflows to learn the Flutter version and the checks the project runs, then run flutter pub get, flutter analyze and flutter test. Treat a test that needs the network, such as test/net/dio_test.dart, as an environment limit and say so rather than counting it as a failure. Read the diff with attention to widget rebuilds (ChangeNotifier providers that notify on every mutation, Consumer placed too high), BuildContext used after an await without a mounted check, and state kept in presenters and providers under lib/. Post one review that cites file and line for every finding, separates blocking findings from suggestions, and quotes the analyze and test output with the Flutter version used. Do not push commits.",
  "resources": [
    {
      "url": "https://github.com/your-org/your-app/pull/1"
    }
  ]
}
  1. Get an API key. In the console, open Settings, then API keys, and create a key for your org. Send it in the Authorization header as a Bearer token.
  2. Send the request. POST the body in request.json to https://api.prix.dev/o/{org}/sessions, with your repository as the resources URL. The run starts in its own cloud sandbox.
  3. Read what came back. Follow the run on its live stream or wait for the run.completed webhook, then read the result and its trajectory.

What comes back

What comes back is one review on the pull request. It states the Flutter and Dart versions the sandbox used, quotes the flutter analyze result and the flutter test summary with the network-dependent test named separately, and then lists findings as file and line with a sentence each, blocking ones first: a context used after an await, a provider mutated in a loop, a status code with no entry in _errorMap.

A reviewer checks two things before acting on it. First, that the Flutter version in the review matches what flutter-drive.yml pins, because an analyzer finding can be version-specific. Second, that a rebuild finding points at a widget that really listens to the provider in question, which the file and line make a one-minute check. The transcript on the session holds every command and its output.

No commit lands on the branch. Once the turn has finished, a follow-up message to the same session can ask the agent to apply a finding, and it works from the same sandbox with the dependencies already fetched.

Run it on every pull request opened or pushed

In the console, add an automation to the Project with this prompt and a GitHub trigger on pull_request.opened and pull_request.synchronize. Every push starts a run against that commit; a redelivered event starts nothing. The trigger fires only for pull requests by an owner, member or collaborator, never for forks, and the automation can be set read-only so a review run cannot push.

From your own server, send this request per pull request with its URL in resources and the delivery id as Idempotency-Key. A webhook_url on the API key receives run.completed with the session_url when the review has been posted, and a key allows max_concurrent runs at once, so a burst of pull requests is started as earlier ones finish.

The use case this job follows, with its automation and trigger: Review every pull request.

Why run it on Rush?

This repository's CI runs a driver suite on an iOS simulator and nothing else, so flutter analyze with strict casts and inference, and the widget and accessibility tests under test/, are run by whoever remembers to. A sandbox per pull request runs them every time with the pinned Flutter version, and the review quotes the result instead of asking the author whether they ran it.

Rebuild and async-gap findings are only useful with a line number and the reason, and only trustworthy when the reviewer has the whole provider and presenter in front of them. The agent reads those files in full, cites them, and several pull requests get that attention at once, each in its own sandbox with its own .dart_tool and pub cache.

Frequently asked questions

Which Flutter version does the review use?

The one the repository pins when it pins one. Here flutter-drive.yml names 3.47.4 and pubspec.yaml allows any Flutter from 3.19.0, so the agent installs the pinned version and names it in the review. If the only pin is in a workflow you do not run anymore, say which version to use in the prompt.

Does it run the integration tests and the driver suite?

Not by default. flutter drive and the files under integration_test/ need a device or simulator, which the sandbox does not provide, so the review runs flutter analyze and flutter test and says that the driver suite was not run. A diff that changes test_driver/ is reviewed by reading.

The dio test calls GitHub's API. Is that a failure?

It is reported as an environment limit, not as a regression in the diff. The review names the test, says it needs network access, and suggests a mocked HttpClientAdapter as a follow-up if the author wants it to run anywhere.

Will it comment on localisation or generated files?

Only when the diff touches them. lib/l10n/** and lib/generated/json/** are excluded from analysis by analysis_options.yaml, and DeerLocalizations is written by flutter pub get, so the review does not lint them; a changed .arb file is checked for keys the code uses.

Does it work with Riverpod or Bloc instead of Provider?

Yes. The checks are the same two commands, and the rebuild and async-gap findings come from reading the state code the app actually has. With Riverpod the agent reads providers and ref.watch placement; with Bloc it reads the event handlers and BlocBuilder scope.