mirror of
https://github.com/callstack/agent-device.git
synced 2026-09-14 20:06:34 +08:00
80b4769230
* test(fuzz): structured CLI/Maestro generators that reach command validation and assert error codes (#1781 B2) * test(fuzz): pin the rediscovered #1433 excess-positional case and keep numeric flag samples inside their range * style: apply oxfmt to the new fuzz modules * perf(fuzz): derive the CLI validation surface lazily so unrelated harness paths keep their startup * test(fuzz): resolve validation generators in the run path so corpus replay keeps its small module graph * test(fuzz): weight the CLI budget toward command validation, pin the finite classes as seeds, guard lazy surface derivation * docs(testing): describe the validation lane's layer split, seed-pinned classes, and PR-time gates * refactor(fuzz): split the validation generator into CLI and Maestro modules, mirrored in tests * refactor(fuzz): collapse the flag-shaped mutation classes and seed literals, derive class coverage from declarations * fix(fuzz): hash every case-generation module in configHash, guarded by an import-closure test * test(fuzz): assert CLI command and flag-key coverage against the registry, and close the six gaps it found
113 lines
4.5 KiB
YAML
113 lines
4.5 KiB
YAML
name: Replay Nightly
|
|
|
|
# The device replay suites this workflow used to carry moved to replays-manual.yml (#1781 A1)
|
|
# after failing every scheduled run from 2026-07-24 on. What stays here is what a schedule can
|
|
# still keep honest: the device-free parser fuzz lane.
|
|
|
|
on:
|
|
schedule:
|
|
- cron: '0 3 * * *'
|
|
workflow_dispatch:
|
|
inputs:
|
|
fuzz-iterations:
|
|
description: 'Parser fuzz cases per target'
|
|
required: false
|
|
default: '38000'
|
|
fuzz-seed:
|
|
description: 'Parser fuzz PRNG seed (defaults to the run number)'
|
|
required: false
|
|
|
|
permissions:
|
|
contents: read
|
|
actions: read
|
|
|
|
concurrency:
|
|
group: ci-${{ github.workflow }}-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
jobs:
|
|
# Parser fuzz lane (#1414, validation targets #1781 B2): hostile input into parseArgs,
|
|
# selector parsing, .ad replay scripts, batch --steps JSON, and the Maestro compat parser,
|
|
# plus schema-derived CLI/Maestro validation cases that assert a planted outcome (specific
|
|
# error code, or acceptance) so a silent acceptance is a finding too. Every rejection must
|
|
# be a typed AppError with a non-empty hint, and no case hangs. Device-free, so it rides
|
|
# this nightly rather than owning a workflow; the seed varies per run so the lane keeps
|
|
# exploring, and anything it catches is appended to the checked-in corpus that the unit
|
|
# lane replays (scripts/fuzz/corpus/regressions.json).
|
|
#
|
|
# 38,000 cases/target holds the job's wall-clock at the pre-B2 five-target/50k budget now that
|
|
# seven targets share it: measured on a quiet host, 7x38k = 16.5s against 5x50k = 16.6-16.9s.
|
|
# Chosen by measurement rather than rounding — 33k would have been 2s cheaper but cost the
|
|
# five untouched targets a third of their depth for no wall-clock reason.
|
|
nightly-parser-fuzz:
|
|
name: Parser Fuzz Lane
|
|
runs-on: ubuntu-latest
|
|
timeout-minutes: 20
|
|
steps:
|
|
- name: Checkout
|
|
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
|
|
|
|
- name: Setup toolchain
|
|
uses: ./.github/actions/setup-node-pnpm
|
|
|
|
# A lane whose classifier or watchdog regressed would pass forever. This runs the
|
|
# broken-on-purpose targets first and fails unless each violation is caught.
|
|
- name: Self-check the harness
|
|
uses: ./.github/actions/run-gate
|
|
with:
|
|
gate: fuzz-parsers
|
|
args: |
|
|
--self-check
|
|
--artifact-dir
|
|
.tmp/fuzz/self-check
|
|
|
|
# Runs even when the self-check fails, so the lane always produces a fuzz envelope too
|
|
# (a step that never runs writes no envelope, and monitoring cannot tell that apart from
|
|
# a lane that went dark). The job still fails because both steps report their status.
|
|
- name: Fuzz parsers
|
|
if: always()
|
|
env:
|
|
FUZZ_ITERATIONS: ${{ github.event.inputs.fuzz-iterations || '38000' }}
|
|
FUZZ_SEED: ${{ github.event.inputs.fuzz-seed || github.run_number }}
|
|
uses: ./.github/actions/run-gate
|
|
with:
|
|
gate: fuzz-parsers
|
|
args: |
|
|
--iterations
|
|
${{ env.FUZZ_ITERATIONS }}
|
|
--seed
|
|
${{ env.FUZZ_SEED }}
|
|
--artifact-dir
|
|
.tmp/fuzz/run
|
|
|
|
# Uploaded on pass as well as failure: each subdirectory of .tmp/fuzz holds a
|
|
# run-envelope.json on #1430's shared lane contract (scripts/lib/lane-envelope.ts) —
|
|
# commit/ref/run provenance, tool + config hash, seed, result — which is what freshness
|
|
# monitoring reads.
|
|
- name: Upload run envelopes and failing cases
|
|
if: always()
|
|
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
|
|
with:
|
|
name: parser-fuzz-run-${{ github.run_id }}-${{ github.run_attempt }}
|
|
path: .tmp/fuzz
|
|
if-no-files-found: warn
|
|
|
|
# Never fails the job on its own: a missing envelope is reported as such rather than
|
|
# masking the real failure with a `cat` error.
|
|
- name: Summarize
|
|
if: always()
|
|
run: |
|
|
{
|
|
echo '### Parser fuzz lane'
|
|
found=0
|
|
for envelope in .tmp/fuzz/*/run-envelope.json; do
|
|
[ -f "$envelope" ] || continue
|
|
found=1
|
|
echo "#### $envelope"
|
|
echo '```json'
|
|
cat "$envelope"
|
|
echo '```'
|
|
done
|
|
[ "$found" = 1 ] || echo 'No run envelope was produced — the lane failed before it could run.'
|
|
} >> "$GITHUB_STEP_SUMMARY"
|