# Development ## Building ```bash dart pub get tool/build_release.sh # `dart build cli`, falling back to `dart compile exe` ``` Use the native binary rather than `dart run`: JIT startup is paid on every invocation. On macOS arm64: | Scenario | `dart run` | AOT binary | | --- | --- | --- | | 2-file project | 6.4s | 0.2s | | 814-file Flutter app, end to end | ~23-29s | 10.4s | | per-file p50 on that app | ~50-75ms | 16ms | The binary is about 18MB and self-contained. On a large project the first file costs about 3s of analyzer warm-up (SDK and package summaries). Cutting that would need a persistent analysis cache. ## Tests ```bash dart test ``` The rule suite runs against `test/fixtures/stuck_loading`, a dependency-free project with hand-written stand-ins for BLoC, Riverpod and provider, so it resolves with no `pub get` and no network. Do not add a `package:` import to it. The query inventory runs against `test/fixtures/firestore_queries`, which carries a hand-written `cloud_firestore` skeleton and a gateway adapter. What the suite holds pipe to: - every rule has a positive and a negative control (`rule_contract_test.dart`); - no fingerprint depends on position, and none collide (`fingerprint_test.dart`); - the state model survives JSON unchanged, with identical findings (`state_map_roundtrip_test.dart`); - partial, unreadable and unresolved scans are reported, never passed silently. `test/cpp_front_test.dart` runs the C++ front end on `test/fixtures/cpp_state` and is skipped without Homebrew LLVM. ## Hooks and CI `git config core.hooksPath .githooks` enables the hooks. `pre-commit` runs `scripts/lint.sh --hook` (format and analyze); `pre-push` runs `scripts/pre-push-ci-gate.sh` (format, analyze and the full test suite). Commit messages follow Conventional Commits (`commitlint.config.js`). ## Measuring precision and recall See [rules.md](rules.md#how-precision-is-measured) for `tool/adjudicate.py` and `pipe recall`.