Skip to content

About

GP-02-Bookkeeping-Assistant

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

GP-02 · Bookkeeping Assistant

An NDI AI Employee. Routine bookkeeping across one client engagement — a bank feed read against the documents held for it, every attachment judged on whether it actually stands, postings proposed only where a written rule covers them, and the chases and client replies drafted.

It prepares and never acts. No journal is posted. No document is attached in anybody's accounting system. Nothing is sent to the client. No period is closed. No coding question is decided.

The catalogue for this employee asks for the first two. That deviation is deliberate, and it is named in the reports, in the workbook, in the skill and here — rather than discovered later by whoever was holding the wrong figures.


The two sentences

A bank line is not a transaction, and the firm's own numbers get better when it is booked as if it were

The feed gives an amount, a date and a string somebody's payment system wrote for its own reasons. That string is not a supplier, a category or a tax treatment. What was bought, and whether the tax on it can be reclaimed, come from a document — and until one is attached the line is an obligation rather than an entry.

The harm runs the way it runs in every desk measured on speed. A line booked on a guess closes. It leaves the unbooked list, stops being an exception, moves turnaround in the right direction and costs fewer hours than the one that had to be chased. Nobody complains, because nobody outside the firm can tell a line booked from evidence from a line booked from a plausible counterparty string.

So the figures are printed with that sentence attached. A report giving turnaround, exception count and hours per client on their own would be reporting the reward for the behaviour this package exists to prevent.

Second half: a rule is something a person wrote, on a day, under their name. The specification asks for confident matches to be posted and low-confidence ones queued. This package has no confidence. A posting is proposed because a named rule written on a known day says so, or it is not proposed at all — there is no third category in which this tool's opinion of its own guess counts as evidence. A score between nought and one cannot be asked what it meant, and there is nobody to ask.

And the date that governs is the date the transaction belongs to. Not the day the bank recorded it, and not the day somebody typed the row. Three dates sit on every line; confusing them is how a figure in a March report changes in May.

A receipt that matches on amount is not a receipt for that transaction

Amount is the whole of most automatic matching, and two documents for the same amount in the same month is not a coincidence worth remarking on — it is a Tuesday. Three things have to agree:

the amount within this firm's own tolerance, which is allowed to be nought and at a firm reclaiming tax on every line should be
the counterparty the supplier's name has to appear in the string the bank wrote, not the other way round
the direction of time a document cannot evidence a payment that happened before the document existed

And one thing has to not be true: the document must not already be attached to something else. A document attached twice is a cost recorded twice and a tax claim made twice — which is the duplicate invoicing the specification escalates, seen from the other end. Both lines then have evidence and both reconcile, which is precisely why nothing else finds it.

Second half: tax reclaimed is a claim against the state, and a document from a supplier with no registration number on file is not evidence of one. The claim can still be entered, it still reduces what is owed, and the person assessed for it years later is the client rather than the firm. Each is named one document at a time and never as a percentage, because a percentage cannot be withdrawn.

And a period that has been reported on is closed

A posting dated into it changes a figure a client has already used — quoted to a bank, put in a board pack, paid tax on. Both dates decide it: the same posting entered before the report date is ordinary work done early, and the entry date is the only thing that tells the two apart.


Confidentiality

The unit is the engagement, not the firm.

A bookkeeping desk works a portfolio, and the catastrophic failure is not a leak to the outside world. It is one client's figures appearing in another client's report. Two clients of one firm may be competitors, and neither has agreed to the other seeing anything.

So segregation is structural. One run is one engagement. Every row of every table carries an engagement reference, and a row belonging to a different engagement is refused — naming the table, the row and both references — rather than filtered out. A filter discards silently, and working papers built from whatever survived one would be a partial set of somebody's books with nothing on them to say so.

Inside one engagement, the firm's own reasoning stays in the firm. No rule, no rule note and no account code reaches a chase or an answer: a chase naming the account tells the client which treatment was assumed, and a client who knows the assumption can produce a document that fits it. And a suspicion goes to the engagement partner and never to the person it is about — a chase reads exactly the same whether the document is late or does not exist.

No contact point appears anywhere in this repository, and no partial one either. Two of the four kinds of file here are drafts addressed to a client, so an address in the fixture would be a real address inside a file whose entire purpose is to be sent.


What it produces

papers/working-papers.xlsx — the deliverable. Seven sheets, opening on a note about what the workbook is not, then on the lines with no evidence before any total:

Read me first       what this is and what it is not
Nothing stands      printed first, before any total
Questions           every coding decision, with no proposed answer
Proposed postings   each with its rule, its author and its date
Tax claims          named per document, never as a rate
Closed periods      what would change a figure already sent
The figures         with the sentence attached

Every total is a formula over exactly the rows above it, never a number this package computed. A hardcoded total is right on the morning it is written and wrong, silently, the first time anybody deletes a row.

Four kinds of text file, each holding what the others must not:

chases/      to the client · the payment quoted back, the name of
             who will be in touch, and nothing else
answers/     to the client · their own words verbatim, and a blank
             where a person writes the answer
papers/      the firm's own file · the rules, the account codes,
             the questions, and everything for the partner
proposals/   one per named person · only the work against their
             own name

plus register.txt, which is an output and never an input.


The scripts

mandate_report.py    can this engagement be worked at all       M01–M07
line_report.py       what the bank says, and what is unbooked   L01–L07
document_report.py   what evidence is held, and what it backs   E01–E08
match_report.py      what attaches to what, and whether it      X01–X08
                     should
posting_report.py    what is booked, and on whose authority     P01–P08
period_report.py     what is closed, and the figures            C01–C08
build_papers.py      the workbook, the chases, the answers,     D01–D09
                     the firm's file and the proposals

55 codes · 35 refused switches · 94 named refusals. Exit 0 clean · 1 findings · 3 refused.

python3 scripts/build_papers.py \
  --mandate engagement-mandate.yaml \
  --team firm-team.yaml \
  --policy booking-policy.yaml \
  --out papers/

What it reads

Eleven tables — people, accounts, rules, suppliers, bank_lines, documents, matches, postings, periods, messages, chases — described in references/the-eleven-tables.md, which is generated from the fixture's own headers so it cannot drift from what the scripts read.

Three governing files, all three of which ship empty. The first run of a fresh install refuses once with 28 gaps across all three listed together. Three refusals in a row teaches a reader to fill in one line and run again, and a person doing that never sees the shape of what they are being asked.

what_happens_to_a_line_with_no_document has no default:

leave-it-unbooked nothing enters the books without evidence, and the ledger stops agreeing with the bank by a growing amount every month. A reconciliation that never reconciles is one nobody reads.
book-it-to-suspense the ledger agrees and the unexplained amount sits in one visible place. Suspense then becomes the permanent home of everything nobody chased.

Both cost something. They are not the same firm, and which of those two failures a firm lives with is not this tool's to choose.

Seven settings ship reading no, written out so that switching one on is a deliberate edit somebody made to a file: post_a_journal, attach_a_document_in_the_clients_system, send_the_client_anything, close_a_period, treat_this_tools_own_confidence_as_a_rule, book_into_a_period_already_reported_on, reclaim_tax_with_no_registration_number.


The sample

Sample Input/ — 14 files, an invented firm (Threnholm & Vaize) working an invented client (Calderwick Marine Fittings) over February and March 2026, as at 10 April.

Every person, supplier, account, bank line, document, rule and registration number is invented. That matters more here than it looks: a real fixture in this shape would be one company's bank account for two months, the suppliers they buy from, what they spend on each, the tax they reclaim and the periods they have already reported. Published, it is a competitor's homework.

Sample Output/ — 141 files, 88,801 bytes, including 123 refusals, every one produced by a real run against the committed input.


Checking it

python3 "Build Tools/fixture/verify.py"

722 checks, no failures, deterministic across runs, in both layouts — the build tree and this repository. Eight passes:

COVERAGE every code, every refused switch and every named refusal reached, or recorded with the reason
CONTACT no contact point and no partial one, over 8 probes with 10 planted examples proving the patterns match
PUBLICATION 127 product names blocked, 47 candidates written down as deliberately absent with the reason
MANIFEST the description inside 1024 in characters and in bytes
INVARIANTS read off the written files and off the cells of the workbook, each with a check that the case was not vacuous
NEGATIVE 30 findings go quiet on a variant, 25 have a written reason, 2 start firing on one
MUTATION 11 gone and 8 appears — the second sense is the one that tests a guard the package enforces on itself
HYGIENE width in characters, nothing idle, every variant proved to change something, every workbook total proved to cover exactly the rows above it

117 variants, each with a written purpose and each proved to change what some script prints.


New Digital Intelligence · gp-02-bookkeeping-assistant

About

GP-02-Bookkeeping-Assistant

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages