The PBC list (Provided by Client: the auditor's request list for schedules, reconciliations and documents) is where reporting timelines actually die. Requests arrive as free-text emails, artifacts go back as attachments with no completeness check, and weeks later the auditor says an item was never received. Nobody can prove otherwise, so the controller re-sends, the clock restarts, and the overrun resurfaces at fee negotiation with no evidence on either side. This demo is the governed alternative: a binder where the model may only locate and quote, four deterministic gates decide what is deliverable, a named controller releases every handover, and a hash-chained manifest turns "you never sent us that" into a five-second recomputation.
Synthetic binder · 74 requests vs 76 artifacts · seeded generator + deterministic engine → identical bindings, hashes and ledger on every run
The Financial Education & Research Foundation's audit fee survey put the average public company audit fee at USD 3.01 million, up 6.4% year on year, with ICFR effort the leading driver. Practitioner surveys in the same territory find 57% of companies saying audit support gets harder every year, and PBC delays are repeatedly cited as the top cause of reporting timeline slippage. Behind those numbers sits a workflow that has barely changed in twenty years. The auditor emails a PBC spreadsheet. The controller forwards line items to process owners. Owners email back attachments of assorted periods, entities and versions. Someone maintains a tracker that drifts from reality within days. Then, deep into fieldwork, the auditor re-requests items already sent, because on their side the attachment landed in a different inbox, or an older version, or nowhere.
The obvious 2026 move is to point a language model at the document store and let it answer the PBC list. That fails in a specific, predictable way. A model asked to find "the Q3 bank reconciliations" will find something: a Q2 reconciliation with a similar filename, last year's Q3, the right file for the wrong entity. Matching is exactly the kind of plausible-similarity task models are fluent at, and audit evidence is exactly the domain where plausible similarity is the failure mode. A near miss delivered with a confident cover note is worse than a gap, because the gap gets chased this week and the near miss gets discovered in March.
The three seeded defects in this demo are the three classic near misses. A covenant certificate renamed "as at 31 December" whose content is dated 30 September. An inventory listing labelled SGP02 whose entity field says SGP01. A fixed asset register that misses the trial balance by S$4,820.00 because a disposal was never removed. A retrieval model matches all three happily, since it reads labels. The gates read content, and content is where evidence lives.
Every system on this site draws the same boundary: deterministic code computes and decides, the model only locates facts and phrases language, and a named human gates anything consequential. Applied to the auditee-to-auditor handover, the pipeline looks like this:
Two design choices carry the weight. First, the abstention rule is absolute: if no artifact meets the request's period, entity and scope, the model outputs "no matching artifact in binder" and the item moves to Open Items. It may not offer a near miss, because a visible gap gets chased and an invisible substitution gets audited. Second, a failed gate has no override. The controller's Deliver button simply does not exist for a binding the code refused, which converts "please just send it, we are late" from a pressure point into a non-event.
This is the gate function from the shipped file, minus the display strings. Every proposed binding runs through it, the visitor's typed requests included. Note what is absent: no confidence score, no weighting, no way for eloquence to move the outcome.
// gates 1-3: pure decisions over the ARTIFACT'S ACTUAL FIELDS (the model // matched on the claimed label; defects live in the gap between the two) function runGates(req, a, tb) { const period = a.actual.p0 >= req.w0 && a.actual.p1 <= req.w1; const entity = a.actual.e === req.e; let tie = "n/a"; // only when a TB linkage is declared if (a.tb === "0000") tie = Math.abs(rowSum(a.rows)) <= TIE_TOL; // a TB ties to zero else if (a.tb) { const target = Math.abs(tb[req.e][a.tb].b); // control account, cents tie = Math.abs(rowSum(a.rows) - target) <= TIE_TOL; // TIE_TOL = 100 = SGD 1 } return [period, entity, tie]; // gate 4, SHA-256, is awaited by the caller } // a binding is deliverable only if no gate is false; there is no override arm const deliverable = gates.every(g => g !== false);
The seeded run processes the full register: 74 requests against a binder of 76 artifacts across two entities. The engine delivers 65 requests (73 artifact hashes on the manifest, since the quarterly bank reconciliation requests each carry three monthly files), blocks 3 where a gate failed, and abstains on 6 where the binder holds nothing responsive. The 422-entry ledger chains every step, and the median request-to-evidence time computes to 58 seconds from the ledger's own timestamps.
The binder is synthetic and labelled so in the app. The 65/3/6 split exists because I seeded three realistic defects and six genuine gaps into a deterministic engine; the run reproduces identically on every load, hashes included. The 58-second median comes from simulated timestamps inside the run, so it demonstrates that turnaround becomes computable from the ledger rather than claiming any client achieved it. What the demo proves is the control architecture: zero silent gaps is a property of the mechanism, since every request terminates in exactly one of two visible states.
The honest limits are worth stating flatly. The matching model here is a deterministic stub over a small in-browser corpus, standing in for retrieval against a real DMS or SharePoint, where matching is harder and abstention thresholds need real tuning. The hash manifest proves integrity and sequence within the app, and it is nothing like legal non-repudiation: there are no trusted timestamps and no counterparty signatures, so a production version would want both. Real PBC lists run to hundreds of items with multi-entity scoping this demo only sketches at two entities. And the request-list shape borrows from a published PBC template (NAIHC's Prepared-by-Client list), with artifact row schemas modelled on the State of CT Open Expenditures ledger fields, both cited in-app; the figures inside are generated.
Click the dispute button and watch the proof replay. Then type your own request: watch it bind with the gates going green one by one, or hit the renamed covenant certificate and watch a gate go red, or ask for the valuation report and watch the model abstain instead of improvising. Tamper with an artifact and let the recompute find it.
Most of my demos govern what happens inside the company: controls tested, payments gated, reconciliations refereed. This one governs the seam between the controller and the audit partner, which is exactly the pair this site speaks to. The CA/CIA in me has chased these lists and knows how they end; the engineer in me can now make the binder prove itself. The mechanism, bind with recomputed evidence or abstain by name, plus a manifest that turns turnaround into data, is what I would want on either side of the table when the fee conversation starts. If your close is losing weeks to the PBC loop, this is the artifact that ends the argument about whose fault that is, and here it is running.