Skip to main content

5 and 6 · The Bookkeeper drafts, then gate 2

What you are doing. Answering the second question: is this journal entry right? Not the document. The entry.

Part 5 · The row moves to Awaiting posting

Within seconds of the intake approval, the row leaves Needs review and appears under Awaiting posting. The audit trail now shows two events: the approve, then bookkeeper_launched.
Awaiting posting is not posted. This is the tab people misread. A journal entry has been drafted and is waiting for a second person. Nothing is in the ledger.
You never talk to the Bookkeeper, and that is deliberate. It is not a conversational agent: it runs only when a document has cleared the intake gate, and it is absent from the chat picker so nobody can start it by accident.

It is launched, not invoked

The moment you approve at the intake gate, the audit trail records bookkeeper_launched. There is no button for it.

It drafts and stops

It proposes the accounts, the debits and the credits, plus the posting rule that produced them, then pauses at the gate. It cannot post.

You see it only as a proposal

Its entire visible output is the JOURNAL ENTRY AWAITING APPROVAL block. Approving that block is what writes the book.
The Bookkeeper card on the landing reads Live · in pipeline, and links to the review inbox rather than opening a conversation. That label is exact: it is running, it produced every entry in your ledger, and there is no chat surface to open.

Part 6 · Check the entry it proposes

Scroll the detail pane and you get JOURNAL ENTRY AWAITING APPROVAL: the account codes, their names, the debit and credit columns, and the posting rule that produced them.
That read degrades rather than fails when the agent server is unreachable: gate.available becomes false, and the row’s own intake_status and result_entry_id still tell you the truth about whether anything posted. A gate you cannot reach must never look like a gate that has been passed.The makers array is what the UI renders as the rule line under the buttons: it is the list of people who may not release this gate, sent up front so the interface can explain a 409 before it happens rather than after.

Read the draft, not just the total

The entry balances either way, so nothing here catches a wrong account for you.On the walked example the rule for this document type is creates_ap: Dr 1200 / Cr 2000, and on this build it debits the gross 1,100,000 to inventory rather than splitting the 100,000 of input VAT to its own account, even though the document type’s own header says “creates AP + 133”.This is precisely why the second gate exists. If the accounts are wrong, Reject posting. That leaves the document intact and the ledger untouched.

Separation of duties, for real

The people named under the buttons are locked out. On the reference walkthrough, four distinct identities touched one document: one composed it, one signed it, one approved the intake, and a fourth approved the posting. Every shortcut was attempted and every one was refused with a 409. That middle row is not a claim. It was executed on the running system, and this is the screen it produced.
Note that the confirmation box stays open behind the error. Retrying will fail the same way. Hand the row to a colleague instead.And note that the button was not disabled. The rule is printed under the buttons and names you if you are one of the makers, but the control looks clickable (rough edge 4). Nothing can go wrong, because the server refuses it. You just get an error rather than a disabled control.

Posted, and only now is it in the book

Once a second person approves the posting, the row moves to Posted, an entry number is assigned, and the detail pane tells the whole story: who approved the intake, who approved the posting, and a View journal entry link into the ledger.
The answer you get is the database’s, not the run’s report.status and entry_number are re-read from the intake row and the journal entry after the posting run returns, rather than taken from what the run said it did. A document that cannot name its journal entry did not post, and the endpoint refuses to report it as posted.That is not defensive coding for its own sake. An earlier version reported the paused run’s own claim, and a planted {"status": "posted"} on the run’s state made a real reviewer’s approval return HTTP 200, with a green tick and the item gone from the inbox, for a document with an empty ledger. The generalisation the fix produced: only the database may establish an outcome, and the layer that answers a human is the layer that must ask it.
The checker recorded on the entry is derived from the verified session, never from a client-supplied header. That matters most here: this string becomes the ledger’s author and is the value the maker-is-not-checker comparison is made against. A client-chosen actor would defeat the whole posting gate rather than just the intake one.

What you cannot do here, and why

You cannot post twice. The writer applies the exact replay contract, so an identical retry returns the existing entry while changed immutable facts conflict. You cannot post an entry that does not balance, or one that lands in a closed period, or one missing a required dimension. The writer validates 18 GL invariants before any write and refuses with a typed error, writing nothing. You cannot undo this. Once an entry is posted the only correction is a reversing entry. That is why both gates are worth the extra minute.

Next

The entry in the ledger

Where it lands, and how to read what you are looking at.