main gained six PRs (#1-#6) while the audit fixes and the state-map seam
were in progress. Four of main's commits fixed the same bugs this branch
did, independently; where they overlap, the more complete version wins:
- Fingerprints: main's (a0844bb) - catchSite (`member#index` among all
catches), union/owner as identity, a legacy fingerprint so version-1
baselines still match, baseline "version": 2. This branch's `catch`
ordinal is dropped; its fingerprint_test still guards the result.
- verify --rule: main's verifyAnalysis (cf9e0b7), which also reports
findings in skipped files as NOT SCANNED.
- Unreadable SDK: both kept - this branch's up-front probe gives the
early, specific error; main's catch around the analyzer (d6b6bcb) is
the backstop.
- Riverpod: main's "a ref.listen is not a render" (f6411dc) and this
branch's AsyncError patterns / stored-value tracking compose; the
fixture keeps both sets of cases.
Ported onto the model-only rules (the seam), since main edited code this
branch had moved into front-end facts:
- excerpt.dart's literal-free, bounded quoting (a prompt-injection
guard) now also covers front-end facts: SwallowedFailure.handler and
NonSubscribingRead.excerpt, and the C++ front end elides string
literals in handler evidence the same way.
- ScanCoverage: runChecks stays source-free; the model carries coverage
(`scanCoverage` in the state JSON, written by both front ends), so
`check --model` states coverage like a source check.
- main's softened wording (rendered-not-emitted "probably unreachable",
the remove-policy dead-handler text, notifier-field-unread) and the
backticked flag message.
Docs: main's README grew the Firestore query inventory and the output
contract; the README stays short, the inventory moves to
docs/queries.md, and the contract, exit codes, verify and baseline
details join docs/usage.md.
Checked against origin/main on resolved mirrors of the dogfood apps:
nuwayze (102), dex (0) and word_alive (0) identical; tick differs only
by the intended riverpod stored-value fix. On the fixture every
difference is one of this branch's fixes. 214 tests pass.
Second step of the state-map seam. The five ChangeNotifier/provider rules
walked resolved units from runProviderRules. Their recognisers move to
the Dart front end (dart_provider_facts.dart) and emit a NotifierModel
of neutral facts (facts.dart):
- NotifierFact / NotifierMethod / FieldMutation: an observable object
that must announce its own changes, its methods, what each calls,
mutates, and whether it notifies directly. Deliberately general enough
for a Qt QObject or a hand-rolled observer subject.
- consumer sites, provider declarations, member uses per file.
- NonSubscribingRead: every read made while building without
subscribing; whether the type is observable is now the rule's filter,
not the recogniser's.
- ProvidersCreatedInBuild: providers constructed in a build method.
The transitive notify closure, the observable/lifecycle/wrapper
judgements and all messages stay in provider_rules.dart, which no longer
imports the analyzer.
runChecks now takes only a StateFlow: no Project, no FileOutcome. The
scanned directories ride on the model (StateFlow.directories) for the
dead-handler scope claim. facts.dart is exported publicly.
No behaviour change, checked byte for byte: `check --format json` is
identical on the fixture and on resolved mirrors of nuwayze, tick, dex
and word_alive. 128 tests pass.
The brief said "nothing outside the bloc references it" and, under
--policy remove, that removing a dead handler "is safe even without product
context", while the scan defaults to --dir lib. The dead-handler finding
itself was scoped in f0fc57e; these three statements were not. A handler
dispatched only from test/ still read as safe to delete.
The decision line and the dead-handler section now say "nothing under <dirs>"
and tell the reader to rule out unscanned directories (rerun with --dir)
before wiring or removing; the remove-policy remediation in rules.dart says
the same. buildGuidance takes the scanned directories, which the guide
command passes from the project.
`ruleFix` was a constant map that ignored `--policy`. Under the default
wire-up policy the brief's legend still said "Remove the dead handler, or
dispatch the event" and "delete the variant and its emits" as equal choices,
directly above findings whose remediation said removal needs explicit
approval - the legend contradicted the policy the README describes.
ruleFixFor(policy) now returns the legend's fixes for a policy: under wire-up
the choice-bearing rules (emitted-not-rendered, rendered-not-emitted,
event-without-handler, dead-handler, notifier-field-unread) lead with making
the code reachable and say removal needs explicit approval; under remove they
keep offering both. `ruleFix` remains, as the default policy's map.
An unreachable state, handler or control is usually an unfinished feature rather
than dead weight, so pipe now leads with making it reachable: render the missing
states, add the missing branch, dispatch the event from a control. Removal is the
exception and is worded as needing explicit approval instead of being offered as
an equal alternative.
New FixPolicy (wire-up default | remove), threaded through runChecks,
runProviderRules, analyzeProject and buildGuidance, with --policy on check, guide
and suggest. It changes every remediation that presents a choice, the brief's
decision line, and the dead-handler section heading ('Unreachable handlers - wire
up the control' vs 'Cleanly deletable').
It is a policy, not a finding: the same facts are reported either way.
81 tests, including 6 new ones asserting the default and that remove flips it.