Metadata-Version: 2.4
Name: review-ready-gate
Version: 0.1.8
Summary: Stop incomplete workpapers reaching manager review
Author: Ryan Duguid
License-Expression: MIT
Project-URL: Homepage, https://duguid.com.au/tools/workpaper-review-gate/
Project-URL: Documentation, https://github.com/ryanduguid/accounting-review-pipeline/tree/review-ready-gate/v0.1.8/packages/review-ready-gate/evaluation/manager_review_gate
Project-URL: Repository, https://github.com/ryanduguid/accounting-review-pipeline.git
Project-URL: Issues, https://github.com/ryanduguid/accounting-review-pipeline/issues
Keywords: accounting,review,workpapers,australia,quality-control
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Financial and Insurance Industry
Classifier: Programming Language :: Python :: 3
Classifier: Topic :: Office/Business :: Financial :: Accounting
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: dev
Requires-Dist: ruff==0.16.7; extra == "dev"
Requires-Dist: mypy==2.3.1; extra == "dev"
Requires-Dist: pytest==9.1.1; extra == "dev"
Requires-Dist: pytest-cov==7.1.0; extra == "dev"
Requires-Dist: coverage==7.16.1; extra == "dev"
Requires-Dist: build>=1.6.1; extra == "dev"
Requires-Dist: tomli>=2.0; python_version < "3.11" and extra == "dev"
Dynamic: license-file

# 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](https://github.com/ryanduguid/accounting-review-pipeline/actions/workflows/ci.yml/badge.svg)](https://github.com/ryanduguid/accounting-review-pipeline/actions/workflows/ci.yml)
[![PyPI](https://img.shields.io/pypi/v/review-ready-gate.svg?color=5C2D91&labelColor=04001F)](https://pypi.org/project/review-ready-gate/)
[![Python 3.10+](https://img.shields.io/badge/python-3.10%2B-5C2D91.svg?logo=python&logoColor=white&labelColor=04001F)](https://www.python.org/downloads/)
[![License: MIT](https://img.shields.io/badge/License-MIT-4F485E.svg?labelColor=04001F)](LICENSE)

[Browser evaluation](https://duguid.com.au/evaluate/manager-review-gate/) · [Reproduce locally](evaluation/manager_review_gate/README.md) · [Quick demo](#quick-demo) · [Release notes](RELEASE_NOTES.md)

The maintained source is under
[`packages/review-ready-gate`](https://github.com/ryanduguid/accounting-review-pipeline/tree/main/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](https://duguid.com.au/evaluate/manager-review-gate/) or reproduce the [versioned evaluation pack](evaluation/manager_review_gate/README.md) from the fabricated fixtures.

It is the missing upstream step in this stack:

```text
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](https://github.com/ryanduguid/accounting-review-pipeline) 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.

> [!WARNING]
> **Not tax advice.** A `READY` result means no configured gate tripped on the files that were present. A human still decides. See [DISCLAIMER.md](DISCLAIMER.md).

## 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](examples/document-intake/README.md)
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](evaluation/manager_review_gate/README.md).
The [missing evidence evaluation](evaluation/missing_evidence/README.md) 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/`](examples/README.md) 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.

```bash
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

```bash
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.

```bash
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](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 |
| `month_end` | current TB, prior TB, open items, self-review | optional bank rec; 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`.

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:

```json
{
  "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](schemas/self_review.json).

### Open items

```text
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](https://github.com/ryanduguid/accounting-review-pipeline/tree/main/packages/xero-trial-balance-export):

```text
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:

```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.

```bash
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.

## 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.

## Related

- [Monthly Close Controls](https://github.com/ryanduguid/accounting-review-pipeline/tree/main/packages/monthly-close-control-plane) - exception pack once a trial balance is allowed onto the review desk
- [Xero Ledger Review Gate](https://github.com/ryanduguid/accounting-review-pipeline/tree/main/packages/elizabeth-anne-alexander) - zero-network variance boundary for AI-assisted TB review
- [australian-accounting-skills](https://github.com/ryanduguid/australian-accounting-skills) - `workpaper-tie-out` and `bas-preparation` workflows this gate enforces mechanically
- [DrDebits](https://github.com/ryanduguid/DrDebits) - APES 110 / TPB guardrails for any LLM sitting *after* a READY pack

## Development

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

```bash
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.10, 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](CONTRIBUTING.md) for the local verification and data-handling requirements. To cut a release, follow [RELEASING.md](RELEASING.md). Do not tag until you intend to publish.

MIT licensed. Boundary statement: [DISCLAIMER.md](DISCLAIMER.md).
