Commit graph

9 commits

Author SHA1 Message Date
trvon
4f3f22acad
feat(verify): report a deleted subject as removed, not resolved
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
Found by dogfooding on nuwayze main: `pipe verify` against the ratchet
baseline printed

  resolved  1 no longer reported - these fixes are confirmed
    + service_browse_bloc.dart:20  LoadServicesByArtist  [dead-handler]

but nothing was fixed. nuwayze commit ccca9bd1e ("delete dead firestore
query code") deleted the event and its handler. Under pipe's default
wire-up policy, deleting an unreachable handler is the exception that
needs approval, so calling it a confirmed fix overstated what happened.

verify now separates the two. A gone finding is REMOVED when its
subject no longer exists:
- its file is not among the project's files;
- for rules labelled by a declaration (the event/state/union/provider or
  owner class), that name is no longer declared anywhere;
- for dead-handler, the on<Event> registration itself is gone.
Everything else stays RESOLVED. Member-labelled rules (a method or
build) are classed by file only, never guessed. Removed entries still
count as gone for --expect-resolved; they are only named honestly, in
text ("removed   N gone because the code was deleted - not wired up",
a REMOVED section) and JSON (removed, removedEntries).

On nuwayze main the entry now reads REMOVED. Tests: a unit test over
hand-built entries (a wired handler stays resolved, a deleted handler
and a deleted file are removed, a member label is not guessed), and an
end-to-end test that deletes a fixture's dead handler and runs
verifyAnalysis. 216 tests pass.
2026-09-24 20:04:52 -06:00
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
17ff523a11
fix(check): lower confidence where unresolved code can hide evidence
check never looked at analyzer errors, so a project that resolved only
partly was analysed as if it were whole. pipe reads names off the syntax
tree, and an unresolved `Foo()` is a method call rather than a
construction, so a dispatch, emission or construction in such a file
simply disappears - and every finding is an absence claim that the
missing evidence could refute.

Found live during this work: with the Flutter SDK unreadable, nuwayze
resolved with 40,079 analyzer errors (package:flutter unresolvable) and
check still reported 102 findings at full confidence, with no warning.

Now, when any analysed file has an unresolved-reference diagnostic
(uri_does_not_exist, undefined_*, creation_with_non_type, ...):
- check warns on stderr and adds a report note naming the first one;
- a finding is lowered one confidence step, with
  data.confidenceLowered = unresolved-references, when an unresolved file
  mentions one of its identity names (event, variant, union, owner,
  provider, member). pipe's evidence is name-based, so a file that never
  names X cannot hide evidence about X; lowering everything for one stray
  error would bury the signal (a first cut did exactly that, taking all
  102 nuwayze findings down for a single unresolved file).

Validated on mirrors of the dogfood repos with a readable SDK (0 analyzer
errors): nuwayze 102 findings identical to the original main, dex 0,
word_alive 1 and tick 1 (the intended riverpod change). The fixture
test adds an unresolved file that constructs NeverDispatched: its
dead-handler finding drops to medium while UnhandledTap, which that file
never names, stays high.
2026-09-22 14:04:32 -06:00
trvon
70262a8c2f
fix(sdk): refuse an unreadable SDK clearly, and fix the runtime fallback
An SDK directory that exists but cannot be read passed discovery, and the
analyzer then died with a raw FileSystemException stack trace. This
happens for real under macOS privacy protection: a Flutter SDK under
~/Documents stats fine but every open fails, and every app whose
package_config.json names that flutterRoot crashed:

  Unhandled exception:
  FileSystemException(path=.../dart-sdk/lib/_internal/allowed_experiments.json; ...)

The chosen SDK is now probed before the analysis collection is built, and
an unreadable one is a PipeException naming the path, where it was
discovered from, and the --sdk remedy.

The last-resort fallback to the running Dart SDK took the executable's
directory (<sdk>/bin) as the SDK root, which never contains
lib/libraries.json, so it could never succeed. sdkFromExecutable now
maps <sdk>/bin/dart to <sdk>.
2026-09-22 13:36:10 -06:00
trvon
d6b6bcb438
fix(analysis): report an unreadable SDK cleanly instead of crashing
Some checks failed
Lint Commits / commitlint (pull_request) Successful in 17s
Tests / Static Analysis (pull_request) Has been cancelled
Tests / Tests (pull_request) Has been cancelled
ae412a7 routed filesystem failures to exit 2, but the analyzer throws its own
FileSystemException (package:analyzer/file_system), which the dart:io catch
in bin/pipe.dart never matched. A project whose package_config still named a
moved Flutter SDK therefore crashed with exit 255 and a stack trace. Found
by dogfooding on word_alive after its SDK moved.

Analyzer.create now turns that into a PipeException naming the SDK path, with
the remedy to re-run flutter pub get or pass --sdk.
2026-09-22 12:22:03 -06:00
trvon
749aa53348
feat(report): state scan coverage in every check, guide and suggest output
A report listed findings without saying how much of the project they were
drawn from, so "0 findings" from 3 of 40 files looked like a clean project -
and an agent handed a brief had no way to know otherwise.

check, guide and suggest JSON now carry a `coverage` block
{discovered, selected, resolved, failed, skipped, complete}. The guide brief,
the markdown report, the suggest agent plan and both text outputs open with a
one-line coverage note, which says PARTIAL whenever a discovered file was not
analysed.

The note lives in the brief's header, which budget trimming never touches. The
"Coverage (what was actually examined)" caveats section was the first thing
dropped under a tight budget, silently; when it no longer fits, a one-line
pointer now says how many caveats were left out.
2026-09-22 12:05:11 -06:00
trvon
a05d971130
fix(scan): a project of unreadable files reported a clean result
Some checks failed
Tests / Static Analysis (push) Has been cancelled
Tests / Tests (push) Has been cancelled
An unreadable file is handed back by the analyzer as a ResolvedUnitResult with
EMPTY content and zero declarations - not an error, not a failure. pipe counted
that as a successful resolution, so a project whose files could not be read
produced:

  $ pipe index /tmp/unreadable
  files  12/12 resolved  (12 discovered, 0 generated, 0 skipped, 0 failed)
  $ pipe check /tmp/unreadable
  findings  0    exit 0

Verified directly: the same 12 files give `contentLen=0 decls=0` when
unreadable and `contentLen=722 decls=2` when readable, with `failure=null` in
both cases. So `check` reported a clean bill of health for a project it had
extracted nothing from - the same silent-success family as the empty scan and
the truncated scan.

Two changes, both at points where the information exists exactly once:
- the resolution loop detects a unit with empty content for a file that has bytes
  on disk (a genuinely empty file is still fine) and records it as a failure, so
  `filesFailed` becomes reachable and `index` accounts for it:
  `11/12 resolved (12 discovered, 0 generated, 0 skipped, 1 failed)`;
- the outcome summary warns when some files failed and REFUSES when every file
  failed, so a partial result can never be mistaken for a complete one:
    pipe: WARNING 1 file(s) could not be analysed (11 resolved), so results are PARTIAL.
    pipe: no file could be analysed: all 12 file(s) failed to resolve

The normal fixture is byte-for-byte unaffected (no warning, same 20 findings),
so this adds no false positive on readable projects.

Found by probing whether an all-files-fail run was reachable; a chmod-000 corpus
made it reachable and exposed that it had been silently scored as success.

106 tests (was 104), dart analyze clean.
2026-09-20 22:02:20 -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
8fcc0cde42 feat: phase 1 ingestion harness + Phase 2 state-flow IR 2026-09-20 13:46:54 -06:00