Every target executable contains its supervisor, baseline reader and evidence writer. A consumer needs Roc and the compiled target. The Blueprint consumer template supplies pinned compiler downloads, ordinary tasks and a workflow with no package provider. It lives outside the release example URL rewriting path.

roc build --fuzz fuzz/main.roc --output=fuzz-target
./fuzz-target run .roc-fuzz/corpus \
  --report-dir=.roc-fuzz/evidence \
  --time=60 --timeout=10 --max-input-size=4096 \
  --source-revision="$SOURCE_REVISION" \
  --roc-version="$ROC_VERSION" \
  --platform-release="$ROC_FUZZ_RELEASE" \
  --platform-sha256="$ROC_FUZZ_SHA256"

run checks the complete declared example inventory and exact accepted baseline before exploring. A failed example or baseline change skips exploration and records the reason in the same report. Each declared example executes once in a fresh child process. Exploration reuses one instrumented child for efficiency. The parent writes reports even when a child crashes or exceeds its deadline. Example preparation also runs in a supervised child: a crashing or nonterminating maker cannot terminate the supervisor or accept a partial inventory.

Defaults are 60 seconds of exploration, a 10-second per-input deadline and leak checking. check runs examples and baseline checks without exploration; explore runs the search independently. A missing accepted entry reports unbaselined and allows normal execution. Exploration with zero accepted executions is inconclusive and exits unsuccessfully.

Review and accept allocation observations§

./fuzz-target check
./fuzz-target accept --profile=default
./fuzz-target check

The default file uses the target’s declared name, such as my_target_baseline.json, in the current working directory. Use --baseline=PATH to override it. Separate entries hold each supported host target and named profile. Commit the accepted file after reviewing it; CI runs check or run and never accepts changes automatically.

Matching is exact: example names, revisions, input digests, region inventory, metric version and all statistics. A reduction also requires review. Acceptance runs each declared example once and updates the relevant entry atomically only when the complete inventory passes. Rejections, assertions, crashes, timeouts, incomplete observations and write failures cannot accept a partial inventory.

Read the evidence§

An explicit report directory must be absent or empty. Keep it separate from inputs and the persistent corpus. Without an override, each invocation chooses a fresh directory under .roc-fuzz/reports/.

The directory contains:

The report records the metric version, effective execution controls (including leak checking), preparation status, structured finding kinds and each worker’s exact span within the combined log. Inputs are capped at 1 MiB in the friendly commands; the prepared-inventory JSON transport is capped at 32 MiB.

Use the report’s outcome, baseline status, metric validity, trace-rule result and capture completeness independently. Missing diagnostic events do not make allocation statistics invalid; online trace rules continue past capture limits. A killed writer can leave an incomplete capture, which is explicitly identified. When a worker returns no result, metric_valid, trace_rules_passed and capture_complete are null (unknown). Fatal outcomes remain separate from partial evidence in the original snapshot. A detailed replay is separate evidence rather than a reconstruction of the original run:

./fuzz-target replay finding.bin --detailed --report-dir=.roc-fuzz/replay
./fuzz-target measure finding.bin --report-dir=.roc-fuzz/measurement
./fuzz-target retain finding.bin --name previous-crash --output=fuzz/regressions

measure prints a JSON array of named allocation observations. retain keeps the original bytes and prints a Fuzz.saved source declaration plus the decoded representative. Keep the binary witness when storage shape matters.

Exit status 0 means success, 1 means a finding, baseline change or inconclusive exploration, and 2 means an infrastructure/configuration failure. Native child termination remains separate in the report.

Keep project policy in the project§

Enumerate your targets in ordinary Blueprint tasks or a CI matrix. Choose valid input domains in properties or validated types, and use generators for bias. Keep resource bounds appropriate for the operation. Acceptance remains a separate explicit task. The repository’s maintainer toolchain can use Nix while consumer tasks use only fetched compilers and self-contained target executables.

GitHub Actions pattern§

The template under templates/quality/ includes a complete pinned-action workflow for Linux and Apple Silicon macOS. Its verification task builds, checks and explores with run --report-dir=.... Upload evidence even on failure:

- name: Verify quality
  run: blueprint workflow verify
- name: Preserve evidence
  if: always()
  uses: actions/upload-artifact@cf430e030ddbb5b0abf93d22962f4752f3646cd9
  with:
    name: quality-${{ runner.os }}
    path: .roc-fuzz/evidence
    if-no-files-found: error

Coverage limits§

Coverage feedback guides discovery; it is not a proof of correctness or a claim that every source branch ran. A retained corpus and a declared regression inventory serve different purposes: coverage reduction can remove redundant seeds while source-declared examples still run independently on every check. Attempted, accepted and rejected execution counts show whether the property actually exercised useful inputs. A campaign that rejects every input provides no passing evidence.