# Design ## Motivation The state bugs users feel (a spinner that never stops, an error never shown, a tap that does nothing) are whole-program facts: a state emitted in one file and never matched in any other, an event dispatched with no handler anywhere. A per-file linter cannot express them, so pipe resolves the whole project and reasons over all of it at once. pipe is a CLI on `package:analyzer`, not a `custom_lint` or analyzer plugin: plugins have no project-wide index, `flutter analyze` does not report new-system plugin diagnostics, and a CLI can pin its own analyzer version. ## Architecture ```text bin/pipe.dart entrypoint lib/src/cli.dart commands and flags lib/src/errors.dart PipeException (message + remedy) lib/src/project.dart package validation, source discovery lib/src/sdk.dart SDK discovery (Flutter-aware) lib/src/analysis.dart analysis contexts, per-file resolution, scan coverage, unresolved references lib/src/pool.dart bounded-concurrency map over files lib/src/excerpt.dart bounded, literal-free source excerpts lib/src/index.dart resolution health, timing, diagnostics lib/src/model.dart declaration model (`graph`) lib/src/state_flow.dart Dart front end: the state model and its JSON lib/src/dart_facts.dart Dart front end: loading and construction facts lib/src/dart_provider_facts.dart Dart front end: ChangeNotifier / provider facts lib/src/facts.dart language-neutral facts (JSON round-trip) lib/src/rules.dart rules, findings, baselines (model only) lib/src/provider_rules.dart ChangeNotifier / provider rules (model only) lib/src/pipeline.dart analyse -> model -> rules lib/src/verify.dart baseline verification lib/src/suggest.dart fix order lib/src/guide.dart agent brief, markdown report, yams emission lib/src/recall.dart labelled-bug recall lib/src/firestore_facts.dart Firestore query inventory (no rules) lib/src/gateway_facts.dart adapters for declarative query APIs tool/adjudicate.py precision adjudicator tool/cpp_front.py experimental C++ front end tool/build_release.sh native AOT build ``` Resolution mirrors Flutter's own tooling: the context collection is rebuilt when `package_config.json` changes, file changes go through `changeFile` and `applyPendingFileChanges`, and an `InconsistentAnalysisException` gets a fresh session. ## The state model Rules never read a syntax tree. A front end turns code into a state model: unions and variants, where each is emitted and matched, events dispatched and handled, busy paths whose failure is swallowed, and observable objects with whether their mutations are announced. `runChecks` takes only that model. The model is plain JSON, and a model read back from JSON reports exactly what the in-process one does (`test/state_map_roundtrip_test.dart`): ```bash pipe state . --format json > model.json # or any front end that writes it pipe check --model model.json # every rule, baseline and gate ``` A front end for another language only has to write that JSON. ## C++ front end (experimental) `tool/cpp_front.py` reads a compile database with libclang (Homebrew LLVM) and maps each project `enum class` to a union: - **emission**: a reference that produces the value (assignment, atomic `store`, argument, return, initialiser); a cast from an integer is an indirect emission, which makes "never emitted" unprovable for that enum; - **observation**: a `case`, `==` or `!=`; - **swallowed failure**: a busy enumerator written to the method's own state, then a top-level `catch` that neither writes state nor rethrows. Identity comes from clang's resolved AST, so two enums with the same name are never confused. Sources the build does not compile are recorded as unresolved. ```bash tool/cpp_front.py --build-dir /build/debug -o model.json pipe check --model model.json ``` For C++, read `rendered-not-emitted` as "branched on but never produced" (a dead branch, or a state a state machine never enters) and `emitted-not-rendered` as "produced but never branched on" (computed and ignored, or reaching output only through a name table or reflection). Rule messages still say "UI". On yams (375 translation units, about two minutes) a hand check of 13 findings found 12 factually correct and 1 false positive, which the front end now excludes. That sample bounds the false-positive rate; it does not measure it. Controls live in `test/fixtures/cpp_state` and `test/cpp_front_test.dart`, skipped when LLVM is absent.