Commit graph

5 commits

Author SHA1 Message Date
trvon
fb055c9978
merge: bring main into the audit and state-map work
Some checks failed
Lint Commits / commitlint (pull_request) Successful in 19s
Tests / Static Analysis (pull_request) Has been cancelled
Tests / Tests (pull_request) Has been cancelled
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.
2026-09-24 07:33:53 -06:00
trvon
f6411dc23b
fix(riverpod): a listener that reads the value is not a render
Some checks failed
Lint Commits / commitlint (pull_request) Successful in 15s
Tests / Static Analysis (pull_request) Has been cancelled
Tests / Tests (pull_request) Has been cancelled
riverpod-async-gap treated any site that read an AsyncValue as a UI read,
including ref.listen. A listener mirrors the value into a side effect and
draws nothing, so it cannot leave a blank or stale screen. Dogfooding on
word_alive flagged an analytics user-property listener this way.

A gap now needs a ref.watch that reads the value. Listeners still count as
places that handle error or loading.
2026-09-22 15:17:19 -06:00
trvon
939cdcb251
fix(riverpod): recognise AsyncError patterns and follow stored values
riverpod-async-gap judged handling from the text of the expression a
`ref.watch` flows into, which missed two common shapes:

- Pattern handling. A switch with `AsyncError()` / `AsyncLoading()` cases
  - the form pipe's own remediation recommends - did not count as
  handling, and a switch STATEMENT on the watched value was never looked
  at, because the parent climb stops at the first non-expression. A
  provider handled that way in one widget and read with `.value` in
  another was reported as unhandled.
- A value stored in a local. `final v = ref.watch(p); ... v.when(...)`
  stopped the climb at the declaration, so both the handling and the read
  were invisible: a handled provider could be reported, and an unhandled
  one read as `v.value` was missed (a recall gap, not only precision).

The consumer now widens to an enclosing switch statement, the uses of a
declared local are followed through the enclosing body, and
AsyncError/AsyncLoading patterns count as handling.

Fixture: patterned, switched and stored providers (each also read
directly elsewhere, so the controls are not vacuous) were all reported
before and are silent now; storedUnhandledProvider was missed and is now
reported.

Real repos: tick's categoriesStreamProvider is no longer reported; its
categories screen handles it with `catsAsync.when(...)` through a
variable, which the rule's any-site-handles definition counts as handled.
nuwayze and dex unchanged. word_alive's internalUserProvider finding comes
from a commit made to word_alive during this work, not from this change:
the old code reports it too.
2026-09-22 13:45:11 -06:00
pipe-loop
f2949dfbfd feat: rule contracts + recall measurement (5/5 real bugs) + real --fail-on gate 2026-09-20 16:00:10 -06:00
pipe-loop
73fdcfeb95 feat: Riverpod support - provider model, riverpod-async-gap rule, tiered by value type 2026-09-20 14:35:40 -06:00