Skip to main content

Workpaper Review Gate

+----------------------------------------------------------------------+
|                        Workpaper Review Gate                         |
+----------------------------------------------------------------------+
|     Stop incomplete workpapers reaching manager review               |
+----------------------------------+-----------------------------------+
| DR  what it gives you            | CR  what it needs                 |
+----------------------------------+-----------------------------------+
| READY / NOT_READY / BLOCKED      | a pack directory of artefacts     |
| cover sheet for the reviewer     | a self-review JSON from the prep  |
| repeat-finding flags             | -                                 |
+----------------------------------+-----------------------------------+

tests PyPI Python 3.11+ License: MIT

Browser evaluation · Reproduce locally · Quick demo · Release notes

The maintained source is under packages/review-ready-gate in the Accounting Review Pipeline. The review-ready-gate distribution, review-ready command and reviewready import remain compatibility identifiers.

A local, review-first readiness gate for Australian public-practice packs. You point it at a folder of workpapers from a junior, an offshore team, or an AI agent. It tells you whether that folder is allowed to enter manager review.

A public evaluation pack reproduces the v0.1.5 manager-review result on fabricated BAS fixtures. It is a local review aid, not an approval system.

Fabricated proof

Pack Result What the gate reports
examples/bas-ready READY No configured findings. A human still decides whether the pack may proceed.
examples/bas-not-ready NOT_READY Missing GST control export, incomplete self-review and a blocking open item.

Open the manager-facing browser evaluation or reproduce the versioned evaluation pack from the fabricated fixtures.

It is the missing upstream step in this stack:

Incomplete pack
      |
      v
review-ready-gate      <-- maintained monorepo package
      |
      |  READY
      v
Manager review (judgement, risk, client impact)
      |
      v
Monthly Close Controls / payday-super-checker / other engines

Monthly Close Controls answers 'what material exceptions exist on these trial balances?'. This tool answers a prior question: 'is the pack even allowed onto the review desk?' A file can have material variances and still be READY, because the variances are documented. A file with a missing GST control export is NOT_READY even if the numbers look tidy.

It does not connect to Xero, store OAuth tokens, write journals, lodge BAS, lock a period, call an LLM, or claim that a file is correct.

Why this exists

Preparation got cheaper. Review did not. Software, offshore capacity, and AI all increase the number of files that hit the review desk. The scarce resource in a firm is the manager who can actually sign.

Missing tie-outs, open questions in email, an unbalanced trial balance and unresolved findings from last period send a pack back for more preparation. This gate checks for those gaps before the pack reaches the manager.

Quick demo

The repository contains fabricated data only. Do not commit client workpapers.

For optional supporting documents, the synthetic document intake example preserves original bytes, proposed fields and human corrections. It adds document checks to an existing pack without replacing its trial balance or changing the readiness output schema.

For the manager-facing, reproducible BAS evaluation, see the manager review gate evaluation pack. The missing evidence evaluation shows what the gate does when a month-end source is absent, empty, for the wrong period, or edited after the gate ran, including a balanced pack that reaches READY without its bank reconciliation.

examples/ is the assault course: every move the tool has, run against fabricated data, with nothing at stake. Learn the flags here before pointing it at a real pack.

python -m pip install -e ".[dev]"

review-ready gate \
  --profile bas \
  --pack examples/bas-ready \
  --output ../../../review-ready-demo/bas-ready

The ready demo exits 0 and writes 3 files:

  • readiness-summary.md: cover sheet a manager reads top to bottom
  • findings.csv: one row per finding, for Excel or Power BI
  • readiness-pack.json: structured evidence, source hashes, and any supplied acknowledgement
review-ready gate \
  --profile bas \
  --pack examples/bas-not-ready \
  --output ../../../review-ready-demo/bas-not-ready

The not-ready demo exits 2. The GST control export is missing, the preparer has not certified the pack, a blocking open item is still OPEN, and the same missing artefact was OPEN on the prior pack, so it is flagged as a repeat.

review-ready view --pack-dir ../../../review-ready-demo/bas-ready

--output points outside this checkout, and it has to: the command refuses an output directory inside a version-control checkout, exiting 1 without creating anything. A readiness pack names a client's file, its workpaper references and every finding standing between it and manager review, and inside a checkout it is one git add -A away from a history that every clone copies. A .gitignore entry is a convention the next commit can waive, and it does nothing about the copy sitting in the working tree meanwhile. A checkout is any directory at or above the output holding .git, .hg, .svn or .bzr, because the harm is the pack going under version control and every one of those copies a committed pack to each clone. A directory inside the work tree named by GIT_WORK_TREE counts too, since Git can hold its metadata elsewhere and leave a tracked tree carrying no marker to find. A work tree selected some other way, by --work-tree on another process's git invocation or by core.worktree in a repository this command never opens, cannot be discovered from here: the check is a backstop for the location you chose, not a proof that a directory is untracked. An output the command cannot examine, such as one behind a symbolic-link loop or an unreadable parent, is refused on the same grounds rather than assumed safe. The refusal applies to writing only: review-ready view still opens a pack wherever it already is.

Use exit code 0 only for READY, 2 for NOT_READY or BLOCKED, and 1 for a malformed file, an invalid command, an --output path inside a version-control checkout, or an --output path that cannot be written.

To run the gate on a schedule in CI, copy examples/github-actions-readiness-check.yml into .github/workflows/. It runs against a repo-stored synthetic pack, fails the job when the pack is BLOCKED, and reports NOT_READY for a human. A READY result is still not an approval.

What gets gated

Profile Required artefacts Extra controls
bas trial balance, activity statement, GST control GL, open items, self-review 1A less 1B ties to GST control movement; no GST posting dated after period_end
month_end current TB, prior TB, open items, self-review optional bank rec (statement against its own GL balance); prior date earlier; same tenant
year_end current TB, prior TB, tie-out matrix, open items, self-review no UNSUPPORTED statement lines

Optional in every profile: prior_findings.csv. An OPEN prior finding that is still present is marked repeat.

Two limits of these controls. The bank reconciliation compares each account's statement balance with the GL balance the reconciliation itself records; it is not tied to the pack's trial balance, so a reconciliation prepared against an earlier ledger state, or for an account the trial balance does not hold, can still pass. And the GST control file is checked for postings after period_end only: a file from an earlier quarter passes the date check, because the self-review carries no period start.

A READY status means no configured control tripped, and only the controls that ran can trip. Every optional slot with no usable input is therefore listed under 'Controls not run' in the pack JSON and the summary, and counted in the summary's scope block: a month_end pack with no bank_rec.csv in it is READY with the bank reconciliation control not run, which is a different claim from READY with every control run. An optional file that exists but holds no bytes is a finding as well, because an empty file is not evidence; its digest is still recorded, so a reviewer can see which bytes the finding is about.

Filenames inside the pack directory are fixed. Header-only CSV schemas live under schemas/, together with the JSON Schema for self_review.json.

Self-review

self_review.json is part of the pack, not a courtesy. Exact keys, exact assertion names, JSON booleans only:

{
  "preparer_initials": "AB",
  "prepared_on": "2026-04-10",
  "engagement_type": "bas",
  "period_end": "2026-03-31",
  "assertions": {
    "pack_complete": true,
    "tie_outs_done": true,
    "open_items_listed": true,
    "variances_explained": true,
    "self_reviewed": true
  }
}

engagement_type must match --profile. prepared_on must not be earlier than period_end. Any assertion that is not true is NOT_READY. The assertions are necessary, not sufficient: a preparer who ticks pack_complete while the GST control file is missing still gets MISSING_ARTEFACT. The JSON Schema is schemas/self_review.json.

Open items

ItemID,Severity,Owner,DueDate,Status,Description,Resolution

Severity is BLOCKING, EXPLAIN, or TRIVIAL. Status is OPEN or CLEARED. A BLOCKING item that is still OPEN with no resolution blocks the gate. A CLEARED item with no resolution text also fails: cleared means someone wrote down what happened.

Trial balance

The 10-column canonical CSV from xero-trial-balance-export:

ReportDate,Tenant,Section,AccountID,AccountName,AccountCode,Debit,Credit,YTDDebit,YTDCredit

One tenant, one report date, unique Tenant+AccountID. Movement debit must equal movement credit, and YTD debit must equal YTD credit, or the pack is BLOCKED.

BAS tie-out

If both the activity statement and the GST control GL are present:

  • labels 1A and 1B are required
  • 1A - 1B is compared with GST-control sum(Credit) - sum(Debit)
  • a difference beyond --tieout-tolerance (default $0.01) is NOT_READY

This is a cash-style control-account tie-out on the files you supply. It is not a lodgement, not a cash-versus-accruals bridge, and not a substitute for the bas-preparation skill.

Human acknowledgement

Optional --review-note JSON:

{
  "reviewer_initials": "RD",
  "reviewed_on": "2026-04-12",
  "comment": "Reviewed fabricated demo findings only; no client file was approved by this example."
}

reviewed_on must not be earlier than period_end. An acknowledgement is evidence of a human action only. It never changes NOT_READY or BLOCKED to READY.

Viewing an existing pack

review-ready view is the read-only half of the gate: it loads a generated pack, proves the 3 files still agree with each other, and prints the cover sheet. It never writes, renames or deletes anything, and it cannot change what the engine computed.

review-ready view --pack-dir outputs/bas-ready

Before displaying anything it fails closed on: a missing artefact; JSON that is not valid UTF-8, not valid JSON, or carries unknown, missing or duplicated top-level members; a threshold or nested source digest that no longer parses as the writer rendered it; a readiness-summary.md whose overall status, source-evidence digests or review-boundary statement disagree with the JSON (including a second, conflicting status line); and a findings.csv whose header, row count or any cell disagrees with the JSON findings, honouring the writer's formula-injection guard exactly. On success the sheet ends with the SHA-256 of each artefact's exact bytes, so the displayed evidence can itself be archived. Exit code is 0 when a pack was verified and shown, 1 when verification failed.

Comparing two runs of a pack

A pack sent back to the preparer is gated again. review-ready compare verifies both runs with the same checks as view, then prints JSON showing what moved between them: the overall status, each source file's digest, and each group of findings by code and slot. It writes nothing.

review-ready compare --previous-pack-dir outputs/bas-first-run --current-pack-dir outputs/bas-second-run

Each finding group is NEW, RECURRING, CHANGED, NOT_RAISED or NOT_COMPARABLE. NOT_RAISED means absent from the later run, not resolved. A changed tolerance or control coverage is listed under scope_changes, and a finding absent after a tolerance change, from a slot the later run did not check, or from a later pack that states no control coverage, is NOT_COMPARABLE. Tolerances compare by value, so 0.01 and 0.010 are the same setting. Exit 0 means both packs verified and the comparison printed; it is not a readiness verdict. A pack that fails verification, or 2 packs for different engagement types or periods, exit 1. Packs carry no wall-clock time, so the reviewer records who made each change and why.

The comparison records changes to the supplied documents as document_coverage under scope_changes. It matches original files by their hashes and retains duplicate counts. Reordering documents, correcting intake metadata or recording a human review preserves that coverage. A finding absent after its document is removed becomes NOT_COMPARABLE; duplicate copies match in their original order. Findings still group by code and slot, so reordering can move a finding between groups. The source rows show the changed file hashes in each slot. Each recorded document slot must identify exactly one original file. Missing or duplicate original-file evidence prevents comparison and exits 1. Document filenames establish the slots even when evidence labels change. A numbered document label that contradicts its filename also prevents comparison.

Design

  • Exact Decimal arithmetic for money, never binary floating point. The gate fixes its own 28-digit context, ignoring the caller's, and refuses a pack whose trial-balance totals would round instead of comparing them inexactly.
  • Schema, duplicate-key, date, and numeric gates fail closed: a malformed file is exit 1 and writes no pack.
  • Missing or empty required artefacts are findings, not crashes, so the cover sheet can tell the preparer what to send back.
  • Source SHA-256 digests travel with the pack. Each digest is taken from the same immutable byte snapshot the loader parsed.
  • Spreadsheet-facing finding text whose first non-whitespace character is =, +, - or @ is prefixed with an apostrophe.
  • The 3 pack files are staged beside their destinations and moved into place only once all 3 have been written. A failed run does not leave 2 runs mixed together.
  • No wall-clock timestamps in the pack.

Data boundary

  • Use a separate, access-controlled working directory for client source files and outputs.
  • A generated pack cannot be written into a version-control checkout at all. write_review_pack walks up from the resolved --output directory and refuses if any level holds .git, .hg, .svn or .bzr, before it creates anything; a .git file counts as well as a directory, so a worktree and a submodule are checkouts too. A level it cannot examine is refused rather than read as an absence. The library enforces this too, so a caller that bypasses the CLI does not bypass the rule.
  • Keep this checkout limited to fabricated fixtures. Its .gitignore blocks CSVs outside examples/ and schemas/, and blocks all 3 generated pack files by name. That stays as a second line: it catches a pack copied in by hand, which no guard on the writer can see.
  • Do not use this as tax, financial, audit, or legal advice.

Development

Use the locked toolchain. python -m pytest is not what CI runs.

uv lock --check
uv sync --locked --all-extras
uv run --locked --extra dev pytest
uv run ruff check reviewready tests
uv run mypy reviewready
uv build

The test suite covers schema gates, the 3 fabricated engagement packs, empty and incomplete artefacts, GST and bank-rec breaks, unsupported tie-outs, acknowledgement parsing, deterministic pack generation, fail-closed pack viewing, and the command-line exit contract.

Continuous integration verifies the committed uv.lock, runs the test suite on Python 3.11, 3.12, 3.13 and 3.14, then builds and smoke-tests the wheel with the fabricated demo. CodeQL scans the Python source, and Dependabot is configured to propose updates for uv dependencies and pinned GitHub Actions. See CONTRIBUTING.md for the local verification and data-handling requirements. To cut a release, follow RELEASING.md. Do not tag until you intend to publish.

MIT licensed. Boundary statement: DISCLAIMER.md.

Metadata

Release files for review-ready-gate 0.2.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 review-ready-gate 0.2.0
File Size Uploaded
review_ready_gate-0.2.0.tar.gz 148.1 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for review-ready-gate 0.2.0
File Interpreter ABI Platform
review_ready_gate-0.2.0-py3-none-any.whl Python 3 none any Details

Total release size: 197.8 kB

Release files / review_ready_gate-0.2.0.tar.gz

Download URL review_ready_gate-0.2.0.tar.gz
Size 148.1 kB
Tags Source
SHA-256 checksum
How to use checksums
365ed629352417a0d4b19df7e52604b42e4b03f7d3d56a3adf929457be49be09
BLAKE2b-256 checksum
How to use checksums
95ae58f3bb6c35b638bd83b993cb73ff5415fd8ff7523579d756f26cf9aa9fd2
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 Oct 4, 2026.

Transparency log

Release files / review_ready_gate-0.2.0-py3-none-any.whl

Download URL review_ready_gate-0.2.0-py3-none-any.whl
Size 49.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
8fe4e6a34581d8337f8226f3a792e348624b84fdd6e5f3526f6bbe68b1491888
BLAKE2b-256 checksum
How to use checksums
9342b5fb5c7a5a8983f99ea60b445ff7fe3b85d4c87bf9c87d98d4fcadd3e271
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 Oct 4, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.2.0 This release

2 release files

0.1.10

2 release files

0.1.9

2 release files

0.1.8

2 release files

0.1.7

2 release files

0.1.6

2 release files

0.1.5

2 release files

0.1.3

2 release files

0.1.1

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