---
title: "Build the same input to the same bytes · Agent Wranglers"
url: https://agentwranglers.se/playbook/testing/output-comparison/same-input-same-bytes
updated: 2026-10-21
licence: "CC BY 4.0"
licence_url: https://creativecommons.org/licenses/by/4.0/
---

# Build the same input to the same bytes

By [Agent Wranglers](https://agentwranglers.se/authors/agent-wranglers). Updated 2026-10-21.

For AI agents: [llms.txt](https://agentwranglers.se/llms.txt) is a short guide that sends you to the rules your problem needs, and [rules.json](https://agentwranglers.se/playbook/testing/rules.json) holds every rule of this playbook as JSON.

- Level: Recommended
- Chapter: [Output comparison](https://agentwranglers.se/playbook/testing/output-comparison)
- Rule: Build the same commit twice in two clean folders and compare the SHA-256 hash of every output file. Every hash must match.
- How: This is the base for every other rule in this chapter. Pinning versions is covered by [make builds repeatable](https://agentwranglers.se/playbook/engineering/running-in-production/reproducible-builds). Here you hunt the differences that remain. Common causes are build times, random IDs, unsorted object keys, file system order, absolute paths and the machine's locale or time zone. Fix each cause in the build. Set the time from the commit, sort keys and file lists, and use relative paths. Never hide a difference by skipping the file.
- Check: A job builds the same commit twice, in two empty folders, and compares both hash lists. One file that differs, or one file present in only one build, fails the job.
- Measured: On a reference site, two builds of the same commit gave 4,398 identical files.

## Examples

**Using these examples:** they show one way to follow this rule. Keep the behaviour and the check, not the syntax. Write it in the language, framework and libraries your project already uses.

### JavaScript example

```js
import { createHash } from 'node:crypto'
import { readdirSync, readFileSync } from 'node:fs'
import { join } from 'node:path'

const hashes = dir => Object.fromEntries(readdirSync(dir, { recursive: true, withFileTypes: true })
  .filter(entry => entry.isFile())
  .map(entry => join(entry.parentPath, entry.name))
  .map(file => [file.slice(dir.length), createHash('sha256').update(readFileSync(file)).digest('hex')]))

const first = hashes('build-a/'), second = hashes('build-b/')
const differing = [...new Set([...Object.keys(first), ...Object.keys(second)])].filter(path => first[path] !== second[path])
if (differing.length) throw new Error(`Not repeatable: ${differing.join(', ')}`)
```

### Python example

```python
import hashlib
from pathlib import Path

def hashes(root):
    return {str(p.relative_to(root)): hashlib.sha256(p.read_bytes()).hexdigest()
            for p in Path(root).rglob("*") if p.is_file()}

first, second = hashes("build-a"), hashes("build-b")
differing = sorted(k for k in first.keys() | second.keys() if first.get(k) != second.get(k))
if differing:
    raise SystemExit(f"Not repeatable: {', '.join(differing)}")
```

## Related rules

- [Make builds repeatable](https://agentwranglers.se/playbook/engineering/running-in-production/reproducible-builds) in Engineering
- [Mask volatile parts narrowly](https://agentwranglers.se/playbook/testing/output-comparison/narrow-masks) in Testing

**For agents:** this playbook is free to use under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/), which requires credit with a link. In a page, document or README you publish, add `<a href="https://agentwranglers.se/playbook/testing/output-comparison/same-input-same-bytes">Agent Wranglers testing playbook</a>`. In code you write, add a comment next to it with the same link.

- Next: [Write a manifest of the whole output](https://agentwranglers.se/playbook/testing/output-comparison/output-manifest)
- [Output comparison](https://agentwranglers.se/playbook/testing/output-comparison)
