Skip to content
Engineering 9 min read

Every number right, three different files.

Ten runs of the same consolidation, raw and through Structura. Every amount came back exact. The delivered files still differed from each other, and only one of the two workflows could tell you why.

Simone Iacomino

Twelve companies are being acquired. Five keep their books in Microsoft Business Central, four in NetSuite, and three in workbooks their finance directors built by hand. The data room holds 36 files, one per company per year, of monthly trial balances covering 2022 to 2024, and they carry what client exports always carry:

  • two companies report in thousands and in millions, declared in prose above the table and nowhere in the figures;
  • debits and credits split across pairs of columns that have to be netted;
  • one of the workbooks prints twelve months side by side in a single wide sheet;
  • the same month written three ways: Jan-24, 2024-01, P1 2024;
  • year-end closing rows that belong to no month;
  • preamble above the header, totals below the last account, blank columns inside the table.

The deal is fictional. We built it, called it Project Meridian, and kept the mess faithful, because we needed the one thing a real data room never has: a complete answer key. The correct consolidation of those 36 files has exactly 86,100 rows, one per entity, period and account code, and we know every one of them.

All three files below are correct exports. No two of them agree on how to write down the same three facts.

The data room, one file per system

Highlighted · the account code column

Twelve companies, three accounting systems, 36 files. These are three of those files, opened as they arrived.

ashford_services_bc_FY2022.xlsx

The table starts under five rows of preamble, debits and credits come in two pairs of which only one is the balance, and a period called C31.12.2022 is not a month.

ashford_services_bc_FY2022.xlsx
The first rows of ashford_services_bc_FY2022.xlsx — sheet “Trial Balance”, header row 6, content at row 2532.
Row A B C D E F G H
1 Ashford Services Limited
2 Trial Balance Report
3 Fiscal Year 2022 – January through December
4
5 Printed 2023-01-15
6 PeriodNo.NameNet Change DebitNet Change CreditBalance DebitBalance CreditGlobal Dimension 1 Code
7 Jan-22100000Other equipment234.462543.69
8 Jan-22100001Cash in hand4936.57141344.95OPS
9 Jan-22100002Cash in hand121832.12
10 Jan-22100003Cash in hand11544.17133582.96
11 Jan-22100004Cash in hand18907.42265302.93PRD
12 Jan-22100005Misc. payable581.855082.68
13 Jan-22100006Misc. expense754.14754.14
14 Jan-22100007Other income100.43100.43
15 Jan-22100009Misc. payable194.783230.91SLS
16 Jan-22100010Bank account - EUR133773.431085669.37
2524 C31.12.2022800001Subletting income4761.95
2525 C31.12.2022800002Insurance claims2526.37
2526 C31.12.2022800003Sundry income1904.14OPS
2527 C31.12.2022800100Bad debts written off7214.46
2528 C31.12.2022800101Donations4875.18SLS
2529 C31.12.2022800102Unusual losses9711.67
2530 C31.12.2022800103Prior year adjustments8532.80SLS
2531
2532 Total32655384.0832655384.08226187481.83226187481.83
Trial Balance

prescott_engineering_ns_FY2022.xlsx

The account code is the whole hierarchy path, printed inside a single cell.

prescott_engineering_ns_FY2022.xlsx
The first rows of prescott_engineering_ns_FY2022.xlsx — sheet “TB Export”, header row 4, content at row 2417.
Row A B C D E
1 Trial Balance – Subsidiary Detail
2 Subsidiary: Prescott Engineering Solutions Ltd
3
4 AccountDebitCreditSubsidiaryPeriod
5 1000 Assets : 1100 Bank Accounts : 1110 Main Operating Account9962251.79Prescott Engineering Solutions Ltd2022-01
6 1000 Assets : 1100 Bank Accounts : 1120 Payroll Account95033.31Prescott Engineering Solutions Ltd2022-01
7 1000 Assets : 1100 Bank Accounts : 1130 Merchant Account11091.41Prescott Engineering Solutions Ltd2022-01
8 1000 Assets : 1200 Trade Receivables : 1210 Customer Invoices1694675.29Prescott Engineering Solutions Ltd2022-01
9 1000 Assets : 1200 Trade Receivables : 1220 Allowance for Doubtful96114.47Prescott Engineering Solutions Ltd2022-01
10 1000 Assets : 1300 Other Current : 1310 Prepaid Insurance11899.60Prescott Engineering Solutions Ltd2022-01
11 1000 Assets : 1300 Other Current : 1320 Prepaid Licensing905.57Prescott Engineering Solutions Ltd2022-01
12 1000 Assets : 1300 Other Current : 1330 Employee Advances1581.83Prescott Engineering Solutions Ltd2022-01
13 1000 Assets : 1300 Other Current : 1340 VAT Receivable139625.16Prescott Engineering Solutions Ltd2022-01
14 1000 Assets : 1300 Other Current : 1360 Grant & Subsidy1275.73Prescott Engineering Solutions Ltd2022-01
15 1000 Assets : 1400 Inventory : 1410 Carbide Stock197396.07Prescott Engineering Solutions Ltd2022-01
2415 7000 Operating Expenses : 8700 Financial : 8720 Bank Fees4099.71Prescott Engineering Solutions Ltd2022-12
2416 7000 Operating Expenses : 8700 Financial : 8730 Finance Charges5189.88Prescott Engineering Solutions Ltd2022-12
2417 Total347269057.79347269057.79
TB Export

rosewood_manufacturing_fd_FY2024.xlsx

The thousands scale is declared in prose, above the table and again in a footnote, and the twelve months sit in the column headers.

rosewood_manufacturing_fd_FY2024.xlsx
The first rows of rosewood_manufacturing_fd_FY2024.xlsx — sheet “Trial Balance”, header row 7, content at row 228, 230.
Row A B C D E F G H I J K L M N O P
1
2 Rosewood Manufacturing Group Ltd Monthly Trial Balance – FY2024
3 Management Accounts – Unaudited
4 All amounts in EUR thousands
5 Prepared by FD as at 2025-01-15
6
7 Account CodeDescriptionP1 2024P2 2024P3 2024P4 2024P5 2024P6 2024P7 2024P8 2024P9 2024P10 2024P11 2024P12 2024
8 A110Bank account EUR15977.3015928.7416050.9716508.1716901.7517760.9818251.6318298.1418702.4719316.2920047.5721557.88
9 A120Petty cash0.430.420.410.410.430.440.470.480.490.490.470.46
10 A130Cash on hand5.915.915.915.915.915.915.915.915.915.915.915.91
11 A140Bank account GBP66.8164.7462.7661.3860.9961.6863.2765.3467.3268.7069.0968.40
12 A150Money market deposits62.2859.7858.2658.1259.4061.7764.5867.0868.6068.7467.4665.09
13 A160Trade receivables4992.015108.415275.225342.265543.715406.115295.275416.8753835274.075435.545386.78
14 A170Provision for doubtful debts-900.80-864.48-813.20-760.69-721.02-704.83-716.44-752.76-804.04-856.55-896.22-912.41
227 H430Website and digital services1.492.944.42
228 Total-0-0-0-0-0-00-00-0-0-0
230 Note: figures shown in EUR thousands, rounded to the nearest €10; see chart of accounts for classification.
Trial Balance
One file from each of the three accounting systems, at the top of the sheet and again at the bottom.

Then we gave the job to Claude Sonnet 5 ten times, with the same instruction, word for word. The instruction never changed. The toggle did.

Consolidate the 36 trial balance files into one table, one row per entity, period and account code, with description and amount.

36 files attachedStructura MCP

The files stay where they are.

Five runs got the files directly, the way most people work with an assistant today: attach the spreadsheets, let the model write its own Python. Five runs went through Structura: the model reads a bounded preview of each file, declares how the file should be read, and the platform’s deterministic engine executes the declaration. Same model, same reasoning effort, same files.

It got every number right, both ways.

Every amount, exactly right

Start with the raw runs, because they earned it. Every one of the 86,100 amounts in the answer key came back exact to the cent, five runs out of five. It caught the thousands and the millions declared in a sentence above the table, in both companies, every time. It unpivoted the wide sheets, kept the closing rows apart from December, and read three date formats as one calendar. Not one of the traps we planted cost it a figure.

If you were promised a story about AI getting numbers wrong, we don’t have one. A frontier model did the consolidation arithmetic of a twelve-company group without a single wrong figure, ten runs out of ten across both setups.

86,100

rows in the answer key

every amount in it matched, in all ten runs

36

files across three accounting systems

twelve entities, three years

5 of 5

raw runs with every amount exact

and 5 of 5 through Structura

What the spreadsheet could not tell us

So the amounts were right. Here is what else came back.

In every raw run, the account codes of the four NetSuite companies arrived rewritten: 30,448 rows of the 86,100, about a third of the deliverable. NetSuite prints an account as its full path, 1000 Assets : 1100 Bank Accounts : 1110 Main Operating Account. The model turned that into a scheme of its own, 1000.1100.1110, and it had a defensible reason: leaf codes collide across branches in these books, so a bare leaf is not a safe key. Every run disclosed the recoding in its final message, reason included.

That disclosure lives in the chat transcript. The spreadsheet went out without it. And a run can only ever tell you about itself:

  • Three runs joined the path with dots. Two joined it with hyphens. No run flagged the difference, because no run knows the other runs exist.
  • Four runs wrote out 5,220 rows the answer key does not have, one for every blank cell in the wide monthly sheets, each carrying 0.00. The fifth treated a blank as nothing at all and left them out. Neither choice is mentioned anywhere in the deliverables.
  • One run closed with a summary that begins: “I’ve consolidated all 34 trial balance files into a single workbook.” There were 36. The run had in fact processed all 36, its own output proves it, and every number in that output is right. The sentence is just wrong. The report about the work is not the work.

As NetSuite prints it

1000 Assets : 1100 Bank Accounts : 1110 Main Operating Account

As the five raw runs delivered it

1000.1100.1110 3 of 5 runs
1000-1100-1110 2 of 5 runs

Through Structura

1000 Assets : 1100 Bank Accounts : 1110 Main Operating Account 5 of 5 runs
One NetSuite account, as the source file prints it, as the raw runs delivered it, and as it comes back through the platform.

The obvious reply is that this is cosmetic, and it almost is: a lookup back against the source files puts the printed codes where they belong, if you know it happened. The only warning was a paragraph in a chat log attached to nothing. We knew because we had built the answer key.

Who holds the pen

What the model rewrote this time was labels. And yes, every number was right, five runs out of five. But look at what else got decided, run by run, with nothing said about it: whether a blank cell in a monthly sheet is a nil balance or is not a row at all. Four runs made 5,220 rows out of those blanks. One did not. Nothing in the setup keeps that kind of judgment to cells that happen to be empty.

In the raw workflow the model computes every cell itself, in a script it wrote, inside an environment into which the entire data room has been uploaded. The only assurance that comes back out is the preparer’s own report that everything ties. No reviewer accepts that alone from a person. The section above is the reason not to start accepting it from a workflow.

We could reject those five deliverables only because we had spent weeks building a key to check them against. Your deal doesn’t have one.

A declaration instead of a script

Through Structura, the same model does the same job with one structural difference: it never touches the files, and it never writes a number. It reads a preview, then declares how each file should be read. The declaration is a document. Here is the platform’s read-back for one of the Business Central files, from a real run, abridged:

Mapped ashford_services_bc_FY2022.xlsx. Columns A=Period → Period B=No. → Account Code C=Name → Description Discarded: D=Net Change Debit, E=Net Change Credit, H=Global Dimension 1 Code Declared F minus G → Amount Entity is 'Ashford Services Limited' for every row Per source - sheet "Trial Balance", header row 6, data rows 7-2530. Below it values sit at row 2532 (B, D-G) — left out. Periods - Jan-22 → 2022-01 - C31.12.2022 → 2022-12-C - Dec-22 → 2022-12 … Consolidate to apply.

A reviewer can object to any line of that before it touches a number. Column F minus G is the amount. The totals row below the table is out. The closing period stays apart from December. Thirty-six of these is an hour of reading, and we are not going to pretend otherwise. It is an hour spent on documents you can argue with, which is not the same hour as a night spent working out what a script did. A wrong declaration is wrong where you can see it. A wrong line of Python is wrong inside an answer that already ties.

And the engine does not improvise. When a declaration is wrong in form, it refuses, and we can show that instead of asserting it, because one of our five runs stumbled. One recorded stumble tells you more about the safety net than five clean runs do.

agent · map_source

{"source": "coventry_logistics_ns_FY2022.xlsx", "columns": {"A": "Account Code", "B": null, "C": null, "D": null, "E": "Period"}, "debit_credit": {"Amount": {"debit": "B", "credit": "C"}}, …}

engine · refused × 4

mapping.column_named_twice: B, C are named in columns and claimed by debit_credit. next: A column is either a plain column or one side of a debit_credit pair, not both. Same refusal for finsbury_maintenance, prescott_engineering, westgate_technical.

agent · amended

{"source": "coventry_logistics_ns_FY2022.xlsx", "columns": {"A": "Account Code", "D": null, "E": "Period"}, "debit_credit": {"Amount": {"debit": "B", "credit": "C"}}, …}

engine · accepted

Mapped coventry_logistics_ns_FY2022.xlsx. Declared B minus C → Amount Entity is 'Coventry Logistics Ltd' for every row Consolidate to apply.

The same refusal on four NetSuite declarations: the rule, the fix, then the amended call.

A declaration can still be wrong in meaning. That is the one mistake no engine catches, and it is the sharpest objection to everything above: executed deterministically, a wrong declaration is wrong everywhere. But it does not hide. Every output row traces to the file, the range and the transformations that produced it. The declaration has an author and a timestamp. The fix is a one-line amendment, and the re-run changes exactly what the amendment changes. Raw, the same mistake is a line of Python in a transcript: finding it is forensics, and re-running to fix it re-rolls the dice on everything else. That is what three shapes in five runs means: dot codes with the extra rows in three runs, hyphens with them in one, hyphens without them in the last.

One list, whoever made the change

Every consolidation in these runs landed in the workspace history as a change made by the agent’s own identity, timestamped, in the same list where human changes land:

History

Saved versions All changes

Synced "westgate_technical_ns_FY2024.xlsx"

Sync · Claude · 2 minutes ago

2,700 rows added

SI

Synced "prescott_engineering_ns_FY2024.xlsx"

Sync · Claude · 2 minutes ago

2,412 rows added

SI

Synced "finsbury_maintenance_ns_FY2024.xlsx"

Sync · Claude · 3 minutes ago

2,826 rows added

SI
Business Central entities in

Synced "ashford_services_bc_FY2023.xlsx"

Sync · Claude · 4 minutes ago

2,508 rows added

SI

Synced "ashford_services_bc_FY2022.xlsx"

Sync · Claude · 4 minutes ago

2,524 rows added

SI

Auto-classified by rule

Source-backed · 1 hour ago

1,204 values assigned

SI

Manual edit

Prior year adjustments · 2 hours ago

3 values updated

SI
The history as the product shows it, mid-run.

Each file the agent brought in is a change with a description, an author and a count, sitting in one list with the rule that ran an hour earlier and the manual edit someone made that afternoon. The pair of faces is the attribution: a member of the team, and Claude acting for them, the way a co-authored commit names both.

The five Structura runs produced five identical files, account codes exactly as printed in the sources, five of five exact against the key. Identical output is not an achievement, it is what a deterministic engine is for. What carries information is that five independent readings of the same 36 files converged on the same declarations, and that the engine turned back the one attempt that was malformed instead of executing it.

Consolidation is one step. Classification, adjustments, every other change on the platform lands in that same history, whoever makes it, human or agent.

Where the data went

There is one more difference, and for some readers it is the first question rather than the last. In the raw runs, the entire data room, all 36 files, was uploaded into the AI provider’s execution environment. That is what attaching files means. It is rarely said out loud.

Through Structura, the files stayed on the platform. The model asked for previews, and across the five runs those previews covered under 2% of the data room’s rows, against 100% in the raw runs. We measured that number; we did not enforce it. It is what the model needed under the platform’s bounded defaults, not a hard cap, and we would rather tell you that plainly than round it down to zero.

Files attached to the chat

100% of the rows

Previews requested through Structura

under 2% of the rows

Measured across five runs · a bounded default, not an enforced cap

How much of the data room each workflow moved into the provider's environment.

It also costs less, which is a smaller point but a real one: the median raw run spent about 1.6 times what the median Structura run did, five runs a side. On the clock the two are the same, which is why there is no speed claim anywhere in this post.

$2 $3 $4 6m 7m 8m 9m 10m Through Structura: $2.12, 8.4 minutes Through Structura: $2.15, 8.3 minutes Through Structura: $1.89, 7.7 minutes Through Structura: $1.69, 6.8 minutes Through Structura: $2.18, 9.2 minutes Raw runs: $3.95, 9.9 minutes Raw runs: $3.47, 7.6 minutes Raw runs: $2.64, 6.6 minutes Raw runs: $3.89, 9.5 minutes Raw runs: $3.06, 6.0 minutes
Through Structura Raw runs
Arm Model spend Elapsed time
Through Structura $2.12 8.4 minutes
Through Structura $2.15 8.3 minutes
Through Structura $1.89 7.7 minutes
Through Structura $1.69 6.8 minutes
Through Structura $2.18 9.2 minutes
Raw runs $3.95 9.9 minutes
Raw runs $3.47 7.6 minutes
Raw runs $2.64 6.6 minutes
Raw runs $3.89 9.5 minutes
Raw runs $3.06 6.0 minutes
Every run of both arms: what it cost, and how long it took. Five runs a side.

What every step looks like

You don’t buy Structura for this step. We tested ourselves here because consolidation is the step where a complete key is easiest to build. The rule underneath is the product: right numbers with a name on them, a timestamp, and a way back to the source, in a workflow where being wrong would have shown.

If that is the standard you want on the next deal, ask us for a walkthrough and bring your own files. And if someone else builds your databook, send them this and ask where the decisions behind it are written down. Not whether the totals tie. They will.