# Reject a truncated video and retry only the artifact download

Separate file-delivery recovery from generation recovery. A downloaded HTTP 200 can contain a broken video; retry the same known artifact identity in this offline fixture.

This complete kit uses Node standard-library modules only. Install/use your own FFmpeg; the binary is not redistributed. Set FFMPEG_BIN to its absolute path if it is not on PATH.

Run from this directory and select a NEW output directory. Existing output directories are refused. On a memory-managed workstation, put its local serialization wrapper before the Node command. No provider credentials or off-device fixture requests are used.

```sh
node run-recipe.mjs --out ./my-run
```

Recorded execution: 7 checks passed, including 1 expected rejections. Node v24.18.0. ffmpeg version 6.0 Copyright (c) 2000-2023 the FFmpeg developers

Preparation

- Prepare Node.js and FFmpeg. The original two-second 320×180 source has twenty frames; its byte size and expected SHA-256 are captured in artifact-manifest.json.
- Inspect truncated.mp4: it is an actual byte-prefix truncation of the original source, not a fabricated error label. The first local download returns those broken bytes.
- Run in a fresh directory. All HTTP requests use one ephemeral 127.0.0.1 artifact URL, with a three-second timeout, a 2-MB response cap and finally cleanup. No production task is called.

## 01. Keep the existing task and artifact identity

Read the manifest for local-paid-once and /artifact/local-paid-once. The fixture exposes an intentionally forbidden /generate route solely to count accidental resubmissions; the successful path never calls it. This local name does not represent an actual paid task.

## 02. Reject the first complete HTTP response as broken media

The first response is HTTP 200 but contains 17,775 bytes, half of the 35,550-byte source. Run FFmpeg with -xerror and explode error detection through the entire video decode. The actual decoder failure is retained in the negative receipt.

## 03. Retry the same artifact URL once

Fetch the exact same artifact identity a second time. The local server returns the complete original bytes. Check SHA-256 against the manifest, then fully decode twenty frames instead of accepting the network status alone.

## 04. Audit the boundary between retries

Check exactly two downloads, zero generation requests and a closed local server. The bounded fixture has no backoff, signed-URL renewal or provider error classification; a real downloader must use its own service’s documented recovery rules.

Acceptance

- The first HTTP 200 response contains the actual truncated byte prefix and differs from the expected SHA-256.
- Full FFmpeg decode of the truncated attempt exits with a recorded error.
- The second fetch uses the same artifact URL and the total download count is exactly two.
- The second file’s SHA-256 equals the complete artifact manifest.
- The recovered file fully decodes to twenty 320×180 frames.
- No /generate request occurs and the loopback server closes; all seven checks pass.

Troubleshooting

HTTP status is 200 but the player cannot open the file. Keep the downloaded bytes and run the full decoder; retry only the same artifact delivery after the media check fails.

The second file is readable but has a different checksum. Verify only the artifact-to-manifest binding before accepting it. Readability does not prove it is the expected task output.

A file retry creates another generation request. Change the retry target back to the saved artifact URL and rerun the zero-generation assertion. Keep generation submission outside this delivery recovery loop.

Public receipt copies normalize only local checkout paths and include publicPathNormalization with the original receipt SHA-256; raw originals are retained outside public assets. The actual receipts are under executed/. failure-receipt.json lists expected negative cases, not failed-suite assertions. Original tiny fixtures are generated again into your new output directory; shipped executed inputs and outputs let you inspect the recorded run. Subprocesses have a 15-second timeout, an 8-MB output cap, argument arrays and no shell. Temporary raw buffers and local HTTP servers are cleaned in finally.

checksums.json records every distributed file except itself and the archive, avoiding self-reference. The ZIP includes that same checksum list. Recompute hashes after download; comparison requires trusting the expected list. execution-receipt.json records executed script hashes before and after the run. The complete kit archive is also bound by the publishing catalog asset inventory.

The local replay proves recovery from this truncated MP4 while preserving artifact identity. It does not show a provider retry promise or a real payment, generation or signed-download execution.

Sources read on 2026-10-14

- FFmpeg command-line documentation: maps, codecs and error exit: https://ffmpeg.org/ffmpeg.html
- Node.js HTTP API: server lifecycle and requests: https://nodejs.org/api/http.html
- Node.js child process API: bounded argument-array execution: https://nodejs.org/api/child_process.html
