Skip to main content

numaudit

Every number you wrote should come from your data.

pip install numaudit
numaudit README.md --data results/

numaudit reads a Markdown or text document, finds every number written in it, and looks for each one in your data files (JSON, JSONL, CSV, TSV, plain text). A number counts as sourced when some data value rounds to it at the precision it was written with: 3,727 is sourced by 3726.69, 2.81 by 2.8125, 29.3% by 0.293. Numbers that appear nowhere in the data are listed with their line, and the command exits with status 1, so it can run in CI before a report or README is published.

It exists because of a real mistake. A benchmark report said a server region took 727-989 ms per tick. The server had printed 3,828.71 MSPT, and the parser that built the summary read only the part after the thousands separator. Run on that summary with the raw logs as data, numaudit flags both numbers:

     91  unsourced 989.0        (nearest in data: 990)
         | 256 | folia | 0.24 / 0.26 | 989.0 / 726.7 | 1.23 / 1.19 | [1, 1] |
     91  unsourced 726.7        (nearest in data: 727.3)

What it reads as a number

  • Thousands separators, decimals, percentages and ranges: 3,727, 2.81, 0.293%, 3,727-4,110, 3,727〜4,110
  • Numbers next to Japanese or other non-ASCII text: 約11倍 gives 11
  • Units glued to a number: 4,110ms, 20x, 1.5GB

It skips what a reader would not take as a measured claim, and lists these as skipped: fenced code blocks and inline code (unless --include-code), URLs and link targets, version strings (v1.1.0, 26.1.2), dates and times, list markers, issue and PR numbers (#4879), and numbers that are part of a name (i5-14600KF, gpt-4).

Options

numaudit DOC [DOC ...] --data PATH [PATH ...] [--allow VALUE ...] [--include-code] [--show-sourced] [--json]
  • --data: files or directories. Directories are searched for .json .jsonl .ndjson .csv .tsv .txt. Inside JSON, numbers in strings count too (log lines, report text), and thousands separators are understood.
  • --allow: values that need no source, such as a hardware model number or a random seed.
  • --show-sourced: list every number, not only the unsourced ones.
  • --json: machine-readable output.

Exit status: 0 when every number has a source, 1 when some do not, 2 when a data path is missing.

In the document

  • <!-- numaudit:ignore --> on a line skips that line, for numbers that are derived or come from elsewhere ("about 1 block in 7,000").
  • <!-- numaudit:data results/servers_*.jsonl --> checks the numbers from that line on against only those files (paths relative to the document). <!-- numaudit:data * --> goes back to all of --data.

Limits

A number is only checked for existence. If a written value is wrong but happens to equal some other value in the data, it passes. In a test on a real README with three planted errors and 26 data files (4,593 distinct values), two were flagged and the third (13.9 for 12.9) matched an unrelated value in another file. Scoping that section with numaudit:data to the files it came from flagged it too. The smaller the pool a section is checked against, the fewer wrong numbers slip through, and small integers match something in almost any data set.

Derived numbers (ratios, differences, "about 11x") have no direct source. Mark them with numaudit:ignore, or better, put the derived values into the data the document is built from.

License

MIT

Metadata

Release files for numaudit 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for numaudit 0.1.0
File Size Uploaded
numaudit-0.1.0.tar.gz 12.7 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for numaudit 0.1.0
File Interpreter ABI Platform
numaudit-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 23.3 kB

Release files / numaudit-0.1.0.tar.gz

Download URL numaudit-0.1.0.tar.gz
Size 12.7 kB
Tags Source
SHA-256 checksum
How to use checksums
5d96e9f1cf53af48b90142161db24d459d9205490ae48d3ea2c0938ac121095b
BLAKE2b-256 checksum
How to use checksums
1eb7ee19848de8d0923019282a58de4ff27f3517b3baad9663e6909ee436e669
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 30, 2026.

Transparency log

Release files / numaudit-0.1.0-py3-none-any.whl

Download URL numaudit-0.1.0-py3-none-any.whl
Size 10.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c71ecd0479608e30e7a4351d5eca86f451c7a6bf225cce8015026ad090dc57a4
BLAKE2b-256 checksum
How to use checksums
e23852042ab2d3c6c299f1111efa8dd84fcd6f0851e391564d896753c6cf3fd6
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 30, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page