Factory docs, home
Page navigation

Pre-release documentation. Proposed designs are labelled in the page; acceptance evidence is maintained in the build review. Local paths appear as placeholders. Current state: Build review.

This page shows how to check a receipt's hashes against the repository, find every receipt that covers a file, write a new receipt skeleton, and read the readability report. Both tools are tested repository scripts (scripts/receipt-check.ts, scripts/readability.ts), not archive scripts. There is no npm script for either tool, so run them with node.

Check one receipt

node scripts/receipt-check.ts check docs/<name>-receipt.json

Receipt shapes are not uniform (docs/agents/factory-model.md section 7): some pin source and test hashes under files or final_sources; some hash only review and browser evidence under evidenceSha256. The checker does not guess which field holds paths. It treats a JSON object as a hash map only when every value is a 64-character hex string, wherever the object sits in the receipt.

A 64-character hex string that sits anywhere else, such as a lone scalar field or a value beside non-hash fields, is never silently dropped. The report lists every one of these as unattributed (field path and value, with no path to check it against), and the receipt's shape is partial whenever unattributed is not empty, even when some other field in the same receipt is a recognised map. A receipt with no hash anywhere, recognised or not, is shape: "unknown". Unknown is never reported as trivially matching, and an unattributed hash is never silently ignored (Factory rule 1). Unknown is never zero.

A map key that is a label rather than a file path is reported as missing; that does not establish a lost file.

For each path a recognised map holds, the report gives one of five states:

StateMeaning
matchesThe file exists and hashes the same
changedThe file exists and hashes differently
missingNo such file exists
unreadableThe path exists but is not a readable regular file, most often a directory
not-checkedThe path is a symbolic link (never followed, so it can never hash a file outside the repository), or is outside the repository: absolute, escaping through .., or under <local evidence directory>, which holds ignored session evidence, not stable repository content

Exit status:

  • 0: every checked path matches, unattributed is empty, and no path is unreadable.
  • 1: no unattributed hash and nothing unreadable, but at least one path changed or is missing. A changed path is not always a fault: receipts are history, so an old receipt's files are expected to have moved on since.
  • 2: the shape is unknown or partial, or at least one hashed path is unreadable. This is never conflated with an ordinary mismatch: unknown or unreadable means the tool could not fully account for the receipt's hashes, not that it found and confirmed a difference.
  • 64: invalid arguments or JSON, or a refused new request (including an empty file list).
  • 70: any other error, such as the receipt file itself not existing.

Find what covers one file

node scripts/receipt-check.ts coverage src/repository.ts

Lists every docs/*-receipt.json that hashes the given path, with its state. This read-only report exits 0 and never changes a receipt. Receipts that cannot be read or parsed appear in unreadable, with a reason. An unreadable hashed file has its own entry state; it does not make its receipt unreadable. An empty coverage list therefore retains gaps in receipt coverage.

Write a new receipt skeleton

node scripts/receipt-check.ts new --out docs/<name>-receipt.json --files <path> [<path> ...] [--base <commit>] [--component <name>]

The command writes:

  • each given file's SHA-256;
  • base: the given commit, or the current git rev-parse --short HEAD, or unknown outside a Git repository;
  • branch: the current Git branch, or unknown;
  • component: the given name, or the output file's own name with -receipt.json removed;
  • the fixed disposition implemented-awaiting-independent-review;
  • an empty review list.

The command checks every file before it writes anything. It refuses, and writes nothing, in five cases: the file list is empty; the output already exists; a given path leaves the repository; a given path is a symbolic link, directly or through a symbolic-linked ancestor directory; a given file cannot be read. The same escape and symbolic-link checks apply to --out itself and --files, so the command can never write outside the repository, even through a symbolic-linked ancestor directory the destination path passes through. It never edits an existing receipt; a fresh review still adds a new one, kept alongside the old as history.

Read a readability report

node scripts/readability.ts [root]

The Node tool follows the archived Python script and counts the same files: README.md, AGENTS.md, CLAUDE.md, handover.md, and docs/*.md, docs/agents/, docs/guide/, docs/brand/, docs/ui/design-system.md, docs/tutorials/, docs/how-to/, docs/explanation/ and docs/reference/. The columns show words, prose words outside fenced code and table rows, sentence count and length (mean, 90th percentile, the share over 30 and 50 words), the share of words in tables and in fenced code, second-person words, sentence-initial imperatives, and words that date a claim. root defaults to the working directory, so run it from the repository root. The report supports review. It marks a file it cannot read as unavailable, and the command always exits 0.

Source: docs/how-to/check-receipts.md