Convention Check (Flutter/Dart — Code)
Audits code changes against the team's six Dart coding conventions and produces a prioritized, actionable report.
For commit message checks, use /commit-check instead.
How to get the diff
Do not ask the user to paste a diff, and do not ask them to select files. Retrieve and analyze everything automatically:
Step 1 — Identify the base branch
- If the user provided a base branch or PR number, use it.
- Otherwise ask: "What is the base branch? (e.g., main, develop)"
Step 2 — Get the full diff
# List changed files (for display only)
git diff $(git merge-base HEAD <base-branch>) --name-only
# Full diff for analysis
git diff $(git merge-base HEAD <base-branch>)
Analyze all changed files. Skip generated files automatically (see below) — do not ask the user to choose.
Only flag lines the diff adds or modifies (lines starting with +). Context lines and removed lines are out of scope.
Files to ignore (auto-generated)
*.g.dart, *.freezed.dart, *.mocks.dart, *.gen.dart, *.config.dart
- Anything under
build/, .dart_tool/, ios/Pods/, android/.gradle/, .flutter-plugins*
pubspec.lock
If the diff is dominated by generated files, mention this and focus on the hand-written changes.
The six rules
Evaluate all six rules. Collect all violations before writing the report.
Rule 1 — Naming conventions (camelCase / PascalCase / snake_case)
| Entity |
Style |
Examples |
| Variables, functions, parameters, methods |
lowerCamelCase |
userCount, fetchProfile(), defaultTimeout |
| Classes, enums, typedefs, extensions, mixins |
UpperCamelCase |
UserService, OrderStatus, Disposable |
| Files, directories, library names |
snake_case |
user_service.dart, login_screen.dart |
| Private identifiers |
leading _ |
_internalCache, _LoginFormState |
| DB / persistence table names |
snake_case |
user_accounts, order_items |
| Import prefixes |
snake_case |
import 'foo.dart' as my_prefix; |
Key checks:
- State classes:
_MyWidgetState for MyWidget extends StatefulWidget — flag missing leading underscore.
- Constants:
lowerCamelCase (not SCREAMING_SNAKE_CASE).
- Acronyms:
HttpClient, parseJsonResponse, userId — flag HTTPClient, parseJSONResponse, userID.
- File names: a file named
LoginScreen.dart is wrong even if the class inside is correct → login_screen.dart.
- Persistence layer: SQLite/Drift/Isar/Hive table/collection names must be
snake_case.
- Variable/parameter names must be nouns or adjectives — never start with a verb:
final fetchUser = ... → final user; final calculateTotal = ... → final total
- Boolean predicates starting with
is, has, can, should are fine: isLoading, hasError, canSubmit
- Methods and functions may (and should) start with verbs:
fetchUser(), calculateTotal()
Rule 2 — Remove unnecessary commented-out code
Flag: commented-out executable code that serves no purpose.
// final oldResult = legacyCompute(x);
- Large
/* … widget tree … */ blocks left after a refactor.
Don't flag:
// TODO(name): explanation
// reason why this code exists
/// dartdoc comments
- License headers
// ignore: <lint_rule> directives
Heuristic: removing the comment markers produces syntactically valid Dart of the same shape → it's dead code.
Rule 3 — Use meaningful variable and function names
Flag:
- Single-letter names outside tight scope — loop
i, short callback e, coordinates x/y are fine. final d = await getUser() at function scope is not.
- Generic placeholders:
data, info, tmp, temp, foo, bar, obj, val, result — when a more specific name is obvious from context.
- Type-in-name:
userList → users; userMap → usersById; stringName → the actual concept.
- Generic widget names:
Widget1, MyButton, CustomContainer → PrimaryButton, CartItemTile.
- Noise callbacks:
onTap1, handler2 → name by what they do (onLoginPressed, handleRefresh).
Always propose a specific rename, not just "rename this."
Rule 4 — No abbreviations (except widely-known ones: ID, URL, etc.)
Allowed — do not flag:
ID, URL, URI, HTTP, HTTPS, API, UI, UX, DB, SQL, HTML, CSS, JS, TS,
JSON, XML, YAML, CSV, PDF, CPU, GPU, RAM, IO, OS, UUID, ISO, UTC, TZ,
TLS, SSL, DNS, IP, TCP, UDP, REST, gRPC, CLI, SDK, CDN, JWT, OAuth,
MD5, SHA, CORS, env, iOS, MVVM, MVC, DI, IoC, ORM, BLoC, SVG,
PNG, JPG, GIF, RGB, RGBA, DP, SP, PX, FPS, APK, AAB, IPA, ADB, FCM,
APNs, ref (Riverpod Ref/WidgetRef only)
Flag everything else:
| Abbreviation |
Fix |
usr |
user |
btn |
button |
msg |
message |
ctrl |
controller |
txt / txtCtrl |
text / textController |
nav |
navigator |
cfg |
config |
calc |
calculate |
pkg |
package |
pwd / pw |
password |
dur |
duration |
pos |
position |
idx |
index |
len |
length |
arr |
rename to what the list contains |
vm |
viewModel |
repo |
repository (unless referring to a Git repo) |
Borderline → use 検討 (Consider):
ctx — very common for BuildContext but Effective Dart recommends context
auth, config — real words in common use
bloc — fine as a class suffix (LoginBloc); flag as a standalone variable
Rule 5 — Document public APIs with clear comments
Scope
| Zone |
Directories |
Treatment |
| Shared |
lib/services/, lib/utils/, lib/extensions/, lib/widgets/, lib/core/, lib/foundation/, lib/providers/, lib/navigator/, lib/config/, lib/constants/, lib/event_bus/, lib/ttp/ |
Full rule applies |
| Feature |
lib/screens/ |
Lighter touch — only flag if genuinely non-obvious |
Format: Use /// (not //, not /** */). Reference symbols with [SymbolName].
Step 1 — Always skip (never flag):
- Private members (leading
_)
- Test files (
test/**, *_test.dart, integration_test/**)
- Widget state classes (
_FooState) and *State plain data holders (e.g., LoginState, SettingState)
- Trivial overrides:
build, dispose, toString, ==, hashCode, createState
- Generated files (
*.g.dart, *.freezed.dart, etc.)
Step 2 — Skip if method is obviously self-explanatory (all must be true):
- Body is ≤ 3 lines, no
async/await
- No dependency calls (
_repository, _service, _dio, _storage, http, SharedPreferences, etc.)
- No
try/catch, no throw
- Body is a simple field access or single delegation — regardless of return type
void clear() => _cache.clear(); // obvious — skip
bool get isEmpty => _items.isEmpty; // obvious — skip
User get currentUser => _user; // obvious — skip
If any signal is missing → not obvious → proceed to Step 3.
Step 3 — Flag:
| Severity |
When |
| 要修正 |
Public class / mixin / extension / enum / typedef in shared zone with no /// |
| 要修正 |
Non-obvious public method in shared zone with no /// |
| 要修正 |
Top-level Riverpod provider in lib/providers/ with no /// (providers inside lib/screens/ → skip) |
| 要修正 |
Existing /// on a public method whose logic changed — new/removed throws, changed return contract, added/removed parameters — and the doc no longer reflects the new behavior |
| 検討 |
Non-obvious public method in lib/screens/ with no /// |
| 検討 |
Doc comment that just restates the name (/// Gets the user. on getUser()) |
Analyzing logic changes in existing docs (applies to shared zone only):
When the diff modifies a public method that already has ///, compare the existing doc against the new code:
- New exception added → doc must list it under a
/// Throws line
- Parameter added, removed, or semantically changed → doc must reflect the new signature
- Return contract changed (e.g., now returns
null on a new condition) → doc must describe the new contract
- Internal restructuring with no behavioral change → no flag needed
Example:
// 検討 — restates the name, no useful info
/// Fetches the user.
Future<User> fetchUser(String id) async { /* ... */ }
// Good — behavior, edge cases, throws
/// Fetches a [User] by [id] from the remote API.
/// Throws [NotFoundException] if the user does not exist.
/// Throws [NetworkException] on connectivity failure.
Future<User> fetchUser(String id) async { /* ... */ }
// Good — Riverpod provider in lib/providers/
/// Provides the current authentication state.
/// Automatically invalidated when the session token changes.
final authStateProvider = StateNotifierProvider<AuthController, AuthState>(
(ref) => AuthController(ref),
);
Rule 6 — No missing newline at end of file (for new or modified endings)
When to flag: only when the current PR's diff is responsible for the missing newline — that is, when a diff hunk ends with the git marker:
\ No newline at end of file
This naturally covers two cases:
- A newly created file (all lines are
+) that has no trailing newline.
- Existing code modified at the end of a class/file — the last line of the file appears in the diff and has no trailing newline.
Do NOT flag a file where the author only changed the middle — if the last line is not part of any + hunk in the diff, the marker will not appear and it is out of scope for this PR.
Severity: 要修正 — causes spurious diff noise in future changes and breaks POSIX tools.
Fix: add one newline character at the very end of the file.
Severity
Two levels — consistent with pre-pr-review:
| Level |
Examples |
| 要修正 (Must fix) |
Wrong casing, PascalCase file name, dead commented-out code, HTTPClient-style acronym, undocumented public class/service/utility in lib/services/ or lib/utils/, missing EOF newline (\ No newline at end of file) |
| 検討 (Consider) |
Borderline abbreviations (auth, config, ctx), names that could be improved, missing doc on a screen-specific non-obvious method, doc comment that restates the name |
When in doubt, prefer 検討 over 要修正.
Output format
# Convention audit
**Scope:** <e.g. "5 Dart files (2 generated files skipped)">
**Base branch:** <branch>
**Result:** N 要修正 · N 検討
## 要修正 (Must fix)
- **`lib/services/AuthService.dart`** — Rule 1 (file naming). Rename to `auth_service.dart`.
- **`lib/services/auth_service.dart:12`** — Rule 1 (class naming). `class authService` → `class AuthService`.
## 検討 (Consider)
- **`lib/screens/login/login_controller.dart:8`** — Rule 3 (meaningful names). `final data = await fetchProfile()` → `userProfile`.
## ✓ Clean rules
<Rules with no violations.>
If there are zero violations, say so plainly.
What to avoid
- Don't flag unchanged context lines — only
+ lines in the diff.
- Don't add rules the team didn't ask for — no
dart format, no prefer_const_constructors, no import-order preferences.
- Don't review generated files.
- Don't be dogmatic — use 検討 for
auth, config, ctx, ref.
- Don't restate the full rule text — a short reference like "Rule 4 (abbreviations)" is enough.
- Don't reformat code — show short snippets to locate the issue only.
1---2name: convention-check3description: Audits Dart/Flutter code changes against the team's coding conventions (naming, dead code, meaningful identifiers, abbreviations, public API docs) and returns a prioritized report. Trigger when the user asks to review, check, audit, or validate code changes — "check this PR", "review my code", "convention check", "lint this change". Auto-fetches the git diff; the user does not need to paste anything.4---56# Convention Check (Flutter/Dart — Code)78Audits code changes against the team's six Dart coding conventions and produces a prioritized, actionable report.910> For commit message checks, use `/commit-check` instead.1112## How to get the diff1314**Do not ask the user to paste a diff, and do not ask them to select files.** Retrieve and analyze everything automatically:1516### Step 1 — Identify the base branch1718- If the user provided a base branch or PR number, use it.19- Otherwise ask: *"What is the base branch? (e.g., main, develop)"*2021### Step 2 — Get the full diff2223```bash24# List changed files (for display only)25git diff $(git merge-base HEAD <base-branch>) --name-only2627# Full diff for analysis28git diff $(git merge-base HEAD <base-branch>)29```3031Analyze **all** changed files. Skip generated files automatically (see below) — do not ask the user to choose.3233Only flag lines the diff **adds or modifies** (lines starting with `+`). Context lines and removed lines are out of scope.3435## Files to ignore (auto-generated)3637- `*.g.dart`, `*.freezed.dart`, `*.mocks.dart`, `*.gen.dart`, `*.config.dart`38- Anything under `build/`, `.dart_tool/`, `ios/Pods/`, `android/.gradle/`, `.flutter-plugins*`39- `pubspec.lock`4041If the diff is dominated by generated files, mention this and focus on the hand-written changes.4243---4445## The six rules4647Evaluate all six rules. Collect **all** violations before writing the report.4849---5051### Rule 1 — Naming conventions (camelCase / PascalCase / snake_case)5253| Entity | Style | Examples |54|---|---|---|55| Variables, functions, parameters, methods | `lowerCamelCase` | `userCount`, `fetchProfile()`, `defaultTimeout` |56| Classes, enums, typedefs, extensions, mixins | `UpperCamelCase` | `UserService`, `OrderStatus`, `Disposable` |57| Files, directories, library names | `snake_case` | `user_service.dart`, `login_screen.dart` |58| Private identifiers | leading `_` | `_internalCache`, `_LoginFormState` |59| DB / persistence table names | `snake_case` | `user_accounts`, `order_items` |60| Import prefixes | `snake_case` | `import 'foo.dart' as my_prefix;` |6162Key checks:63- **State classes:** `_MyWidgetState` for `MyWidget extends StatefulWidget` — flag missing leading underscore.64- **Constants:** `lowerCamelCase` (not `SCREAMING_SNAKE_CASE`).65- **Acronyms:** `HttpClient`, `parseJsonResponse`, `userId` — flag `HTTPClient`, `parseJSONResponse`, `userID`.66- **File names:** a file named `LoginScreen.dart` is wrong even if the class inside is correct → `login_screen.dart`.67- **Persistence layer:** SQLite/Drift/Isar/Hive table/collection names must be `snake_case`.68- **Variable/parameter names must be nouns or adjectives — never start with a verb:**69 - `final fetchUser = ...` → `final user`; `final calculateTotal = ...` → `final total`70 - Boolean predicates starting with `is`, `has`, `can`, `should` are fine: `isLoading`, `hasError`, `canSubmit`71 - Methods and functions may (and should) start with verbs: `fetchUser()`, `calculateTotal()`7273---7475### Rule 2 — Remove unnecessary commented-out code7677**Flag:** commented-out executable code that serves no purpose.78- `// final oldResult = legacyCompute(x);`79- Large `/* … widget tree … */` blocks left after a refactor.8081**Don't flag:**82- `// TODO(name): explanation`83- `// reason why this code exists`84- `///` dartdoc comments85- License headers86- `// ignore: <lint_rule>` directives8788Heuristic: removing the comment markers produces syntactically valid Dart of the same shape → it's dead code.8990---9192### Rule 3 — Use meaningful variable and function names9394Flag:95- **Single-letter names outside tight scope** — loop `i`, short callback `e`, coordinates `x/y` are fine. `final d = await getUser()` at function scope is not.96- **Generic placeholders:** `data`, `info`, `tmp`, `temp`, `foo`, `bar`, `obj`, `val`, `result` — when a more specific name is obvious from context.97- **Type-in-name:** `userList` → `users`; `userMap` → `usersById`; `stringName` → the actual concept.98- **Generic widget names:** `Widget1`, `MyButton`, `CustomContainer` → `PrimaryButton`, `CartItemTile`.99- **Noise callbacks:** `onTap1`, `handler2` → name by what they do (`onLoginPressed`, `handleRefresh`).100101Always propose a specific rename, not just "rename this."102103---104105### Rule 4 — No abbreviations (except widely-known ones: ID, URL, etc.)106107**Allowed — do not flag:**108```109ID, URL, URI, HTTP, HTTPS, API, UI, UX, DB, SQL, HTML, CSS, JS, TS,110JSON, XML, YAML, CSV, PDF, CPU, GPU, RAM, IO, OS, UUID, ISO, UTC, TZ,111TLS, SSL, DNS, IP, TCP, UDP, REST, gRPC, CLI, SDK, CDN, JWT, OAuth,112MD5, SHA, CORS, env, iOS, MVVM, MVC, DI, IoC, ORM, BLoC, SVG,113PNG, JPG, GIF, RGB, RGBA, DP, SP, PX, FPS, APK, AAB, IPA, ADB, FCM,114APNs, ref (Riverpod Ref/WidgetRef only)115```116117**Flag everything else:**118119| Abbreviation | Fix |120|---|---|121| `usr` | `user` |122| `btn` | `button` |123| `msg` | `message` |124| `ctrl` | `controller` |125| `txt` / `txtCtrl` | `text` / `textController` |126| `nav` | `navigator` |127| `cfg` | `config` |128| `calc` | `calculate` |129| `pkg` | `package` |130| `pwd` / `pw` | `password` |131| `dur` | `duration` |132| `pos` | `position` |133| `idx` | `index` |134| `len` | `length` |135| `arr` | rename to what the list contains |136| `vm` | `viewModel` |137| `repo` | `repository` (unless referring to a Git repo) |138139**Borderline → use 検討 (Consider):**140- `ctx` — very common for `BuildContext` but Effective Dart recommends `context`141- `auth`, `config` — real words in common use142- `bloc` — fine as a class suffix (`LoginBloc`); flag as a standalone variable143144---145146### Rule 5 — Document public APIs with clear comments147148**Scope**149150| Zone | Directories | Treatment |151|---|---|---|152| Shared | `lib/services/`, `lib/utils/`, `lib/extensions/`, `lib/widgets/`, `lib/core/`, `lib/foundation/`, `lib/providers/`, `lib/navigator/`, `lib/config/`, `lib/constants/`, `lib/event_bus/`, `lib/ttp/` | Full rule applies |153| Feature | `lib/screens/` | Lighter touch — only flag if genuinely non-obvious |154155**Format:** Use `///` (not `//`, not `/** */`). Reference symbols with `[SymbolName]`.156157**Step 1 — Always skip (never flag):**158- Private members (leading `_`)159- Test files (`test/**`, `*_test.dart`, `integration_test/**`)160- Widget state classes (`_FooState`) and `*State` plain data holders (e.g., `LoginState`, `SettingState`)161- Trivial overrides: `build`, `dispose`, `toString`, `==`, `hashCode`, `createState`162- Generated files (`*.g.dart`, `*.freezed.dart`, etc.)163164**Step 2 — Skip if method is obviously self-explanatory (all must be true):**165- Body is ≤ 3 lines, no `async`/`await`166- No dependency calls (`_repository`, `_service`, `_dio`, `_storage`, `http`, `SharedPreferences`, etc.)167- No `try/catch`, no `throw`168- Body is a simple field access or single delegation — regardless of return type169170```dart171void clear() => _cache.clear(); // obvious — skip172bool get isEmpty => _items.isEmpty; // obvious — skip173User get currentUser => _user; // obvious — skip174```175176If **any** signal is missing → not obvious → proceed to Step 3.177178**Step 3 — Flag:**179180| Severity | When |181|---|---|182| **要修正** | Public class / mixin / extension / enum / typedef in shared zone with no `///` |183| **要修正** | Non-obvious public method in shared zone with no `///` |184| **要修正** | Top-level Riverpod provider in `lib/providers/` with no `///` (providers inside `lib/screens/` → skip) |185| **要修正** | Existing `///` on a public method whose **logic changed** — new/removed `throws`, changed return contract, added/removed parameters — and the doc no longer reflects the new behavior |186| **検討** | Non-obvious public method in `lib/screens/` with no `///` |187| **検討** | Doc comment that just restates the name (`/// Gets the user.` on `getUser()`) |188189**Analyzing logic changes in existing docs (applies to shared zone only):**190191When the diff modifies a public method that already has `///`, compare the existing doc against the new code:192- New exception added → doc must list it under a `/// Throws` line193- Parameter added, removed, or semantically changed → doc must reflect the new signature194- Return contract changed (e.g., now returns `null` on a new condition) → doc must describe the new contract195- Internal restructuring with no behavioral change → no flag needed196197**Example:**198199```dart200// 検討 — restates the name, no useful info201/// Fetches the user.202Future<User> fetchUser(String id) async { /* ... */ }203204// Good — behavior, edge cases, throws205/// Fetches a [User] by [id] from the remote API.206/// Throws [NotFoundException] if the user does not exist.207/// Throws [NetworkException] on connectivity failure.208Future<User> fetchUser(String id) async { /* ... */ }209210// Good — Riverpod provider in lib/providers/211/// Provides the current authentication state.212/// Automatically invalidated when the session token changes.213final authStateProvider = StateNotifierProvider<AuthController, AuthState>(214 (ref) => AuthController(ref),215);216```217218---219220### Rule 6 — No missing newline at end of file (for new or modified endings)221222**When to flag:** only when the current PR's diff is responsible for the missing newline — that is, when a diff hunk ends with the git marker:223224```225\ No newline at end of file226```227228This naturally covers two cases:229- A **newly created file** (all lines are `+`) that has no trailing newline.230- **Existing code modified at the end of a class/file** — the last line of the file appears in the diff and has no trailing newline.231232**Do NOT flag** a file where the author only changed the middle — if the last line is not part of any `+` hunk in the diff, the marker will not appear and it is out of scope for this PR.233234**Severity:** 要修正 — causes spurious diff noise in future changes and breaks POSIX tools.235236**Fix:** add one newline character at the very end of the file.237238---239240## Severity241242Two levels — consistent with `pre-pr-review`:243244| Level | Examples |245|---|---|246| **要修正 (Must fix)** | Wrong casing, PascalCase file name, dead commented-out code, `HTTPClient`-style acronym, undocumented public class/service/utility in `lib/services/` or `lib/utils/`, missing EOF newline (`\ No newline at end of file`) |247| **検討 (Consider)** | Borderline abbreviations (`auth`, `config`, `ctx`), names that could be improved, missing doc on a screen-specific non-obvious method, doc comment that restates the name |248249When in doubt, prefer **検討** over **要修正**.250251---252253## Output format254255```markdown256# Convention audit257258**Scope:** <e.g. "5 Dart files (2 generated files skipped)">259**Base branch:** <branch>260**Result:** N 要修正 · N 検討261262## 要修正 (Must fix)263264- **`lib/services/AuthService.dart`** — Rule 1 (file naming). Rename to `auth_service.dart`.265- **`lib/services/auth_service.dart:12`** — Rule 1 (class naming). `class authService` → `class AuthService`.266267## 検討 (Consider)268269- **`lib/screens/login/login_controller.dart:8`** — Rule 3 (meaningful names). `final data = await fetchProfile()` → `userProfile`.270271## ✓ Clean rules272273<Rules with no violations.>274```275276If there are zero violations, say so plainly.277278## What to avoid279280- **Don't flag unchanged context lines** — only `+` lines in the diff.281- **Don't add rules the team didn't ask for** — no `dart format`, no `prefer_const_constructors`, no import-order preferences.282- **Don't review generated files.**283- **Don't be dogmatic** — use 検討 for `auth`, `config`, `ctx`, `ref`.284- **Don't restate the full rule text** — a short reference like "Rule 4 (abbreviations)" is enough.285- **Don't reformat code** — show short snippets to locate the issue only.