Skip to content

v0.9: exploration metrics in the live status UI - #2

Merged
ianp94 merged 5 commits into
mainfrom
v0.9-exploration-ui
Jul 20, 2026
Merged

v0.9: exploration metrics in the live status UI#2
ianp94 merged 5 commits into
mainfrom
v0.9-exploration-ui

Conversation

@ianp94

@ianp94 ianp94 commented Jul 20, 2026

Copy link
Copy Markdown
Owner

Summary

Makes coverage-guided exploration visible: the live status screen grows an exploration panel as a fuzzing campaign runs.

exploration demo

Changes

  • StatusReporter: exploration panel (execs/sec, corpus size, finds by classification crash/invariant, time-since-last-find), shown once finds arrive, in both TTY box and piped one-line modes.
  • Fed from the triage layer (FuzzIO.recordSaved on every saved input) — covers the JQF path and corpus replay.
  • JQF harness starts the render thread (previously only TriageSink); calculator fuzz task forwards -Dclosurejvm.status*; CorpusRunner starts the reporter; shutdown-hook final frame for all run types.
  • Demo docs/demo-explore.svg (16 real frames) + new README Exploration section.
  • Roadmap: v0.9 (this) and v0.10.0 — coverage-guided over HTTP (server-side coverage agent), Kubernetes deploy, auto-injection admission webhook, and a web dashboard (some likely a separate repo).

Validation

Live JQF calculator campaign renders the panel: execs=105 (36.9/s), corpus=102, crash finds climbing, last-find timer. Full build + check green on JDK 17.

Scope

In-process JQF coverage guidance stays local here; feeding the app-under-test's coverage back over HTTP is v0.10 (the real "coverage-guided via HTTP requests" vision).

🤖 Generated with Claude Code

ianp94 and others added 5 commits July 19, 2026 23:18
StatusReporter grows an exploration panel (execs/sec, corpus size, finds by
classification crash/invariant, time-since-last-find) once finds arrive, in both
the TTY box and the piped one-line summary. Counters are fed from the triage
layer (FuzzIO.recordSaved on every saved input), so they cover the JQF path and
corpus replay alike.

Wiring: the JQF harness now starts the StatusReporter render thread (it only
started TriageSink before), the calculator fuzz task forwards -Dclosurejvm.status*
into the fuzz JVM, CorpusRunner starts the reporter, and a shutdown-hook final
frame guarantees a closing tally for any run type (JQF/corpus/generic), not just
the runners that call renderFinal().

Verified with a live JQF calculator campaign: the exploration panel renders with
execs=105 (36.9/s), corpus=102, crash finds climbing, last-find timer.

Demo: docs/demo-explore.svg (animated, 16 real frames) shows the panel
materializing as finds arrive; embedded in a new README Exploration section.

Roadmap: v0.9 (exploration+UI) and v0.10.0 (coverage-guided over HTTP via a
server-side coverage agent, Kubernetes deploy, auto-injection admission webhook,
and a web dashboard — some likely a separate repo) added to TODO.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018kqCb2ZpmgPcLhjLo5ZbNx
… row

Two issues surfaced in review of the v0.9 exploration panel:

1. Top 'crashes' counter disagreed with exploration 'finds crash' because they
   were fed from different paths — recordCrash() was only wired into GenericRunner,
   while the crash-find count came from FuzzIO in the JQF/corpus paths. Now a crash
   is counted wherever executeIteration() throws (JQF harness + preseed, CorpusRunner),
   matching GenericRunner. A leak/invariant thrown by endIteration is still counted
   separately, not as a crash. Verified: JQF calculator run now shows crashes=102 ==
   finds crash=102.

2. No coverage percentage. Added StatusReporter.recordCoverage(covered, total) and a
   'coverage X.X% (covered/total edges)' panel row (TTY + piped), shown once a source
   reports. A true % needs a covered/total denominator from instrumenting the code
   under test, so the source is the v0.10 server-side coverage agent (or an optional
   in-process JaCoCo provider) — the plumbing is in place now.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018kqCb2ZpmgPcLhjLo5ZbNx
Regenerate docs/demo-explore.svg (17 real frames) showing the consistent crash
counting (top crashes == exploration finds crash), and update the README
Exploration section to describe the coverage % row and the crash-counter
consistency.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018kqCb2ZpmgPcLhjLo5ZbNx
…sifier)

The crash count was a fuzzing false positive: every exception from a target
counted as a crash, so a parser rejecting malformed input (IllegalArgumentException
'bad char', IllegalStateException 'mismatched parenthesis') flooded the count.
The calculator showed 102 'crashes' that were almost all normal input validation.

Add optional runner.api.CrashClassifier: a target declares which exceptions are
expected rejections. The runners (GenericRunner, CorpusRunner, JQF harness +
preseed) now count expected exceptions as 'rejected' (not crashes, not saved to
the crash corpus); in the JQF path an expected exception becomes an assumption
violation so the fuzzer discards the input. CalculatorFuzzTarget declares
IllegalArgumentException/IllegalStateException expected.

Result on the same run: crashes 102 -> 1, rejected 12. The single remaining crash
is a genuine NoSuchElementException (empty-stack pop on a truncated expression) —
exactly the robustness bug worth surfacing. StatusReporter gains a 'rejected' row.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018kqCb2ZpmgPcLhjLo5ZbNx
…ard findings + Claude analysis

Regenerate docs/demo-explore.svg from a real HTTP-driven exploration run against
JPetStore (220 execs, 0 crashes, 48 server-side invariant finds via the valve).
Rewrite the README Exploration section around driving a real app over HTTP, the
rejected-vs-crash distinction (CrashClassifier), and the runHttpDrive command.

Roadmap: the v0.10 web dashboard should surface actual findings (crash detail,
stacks, invariant violations, saved input, route/metrics — browse triage bundles,
not just counts), and an optional Claude-API-backed analysis of findings/campaigns
(cluster/dedupe, explain stacks, suggest root cause) with a server-side key.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_018kqCb2ZpmgPcLhjLo5ZbNx
@ianp94
ianp94 merged commit 3a87ad6 into main Jul 20, 2026
4 checks passed
@ianp94
ianp94 deleted the v0.9-exploration-ui branch July 20, 2026 03:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant