Replay Nightly #160
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| 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: '50000' | |
| 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): hostile input into parseArgs, selector parsing, .ad replay | |
| # scripts, batch --steps JSON, and the Maestro compat parser. One invariant — every | |
| # rejection is 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). | |
| 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 || '50000' }} | |
| 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" |