> Documentation version: 0.5.0. [HTML page](https://lukewilliamboswell.github.io/roc-fuzz/quality.html)

<a id="add-fuzzing-to-ci"></a>

# Add fuzzing to CI

Every target executable contains its supervisor, baseline reader and evidence
writer. A consumer needs Roc and the compiled target. The
[Blueprint consumer template](https://github.com/lukewilliamboswell/roc-fuzz/tree/trunk/templates/quality) supplies pinned
compiler downloads, ordinary tasks and a workflow with no package provider.
It lives outside the release example URL rewriting path.

``` sh
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.

<a id="review-and-accept-allocation-observations"></a>

## Review and accept allocation observations

``` sh
./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.

<a id="read-the-evidence"></a>

## 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:

- ` report.json `, following [` roc-fuzz-quality/1 `](roc-fuzz-ci-report.schema.json);


- ` summary.md ` and the combined child ` run.log `;


- original example inputs, worker observations and original trace snapshots;


- exploration failure artifacts and ` original-input.bin ` for the last attempted
  input, with capture completeness and its SHA-256 in the report; and

- bounded original-run logical traces, including unfinished regions on failure.


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:

``` sh
./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.

<a id="keep-project-policy-in-the-project"></a>

## 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.

<a id="github-actions-pattern"></a>

### 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:

``` yaml
- 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
```

<a id="coverage-limits"></a>

## 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.

