Case study · August 2026 – present

An order desk that keeps the ERP in charge

An order-operations platform for one branch of a publicly listed commercial-equipment company in international trade, built with AI coding agents on a locked-down desktop. It went into daily production in September 2026.

28 Sep 2026into daily production on the live ERP company
93.4%agreement with the ERP's approvals in a 30-day shadow run
29,202automated checks passing at a late-October release
0admin rights, servers or open ports needed

Dealers send purchase orders as PDFs and emails. Before this platform, a customer-service rep read each one and typed it into the ERP by hand, then chased pick lists, shipping papers and invoices across several tools.

I directed AI coding agents to build a desk that reads the order, keeps a person in charge of every decision, and writes orders, pick lists and shipping documents into the ERP under that person's own account.

At a glance

My role I wrote the specifications, made the business and risk decisions, and verified every release. AI coding agents wrote the code.
Users The branch's customer-service reps, supervisors and managers
Built with Node.js, a static desk app, the ERP's service API (SAP Business One), read-only SQL, PowerShell, Windows Task Scheduler, DPAPI
Constraints A shared Windows host, standard user, no admin rights, no database server, no inbound ports, a forced sign-out every Saturday night
Timeline First work packet in September 2026, daily production from 28 September, more than 100 packets since

How an order moves

  1. Arrives. Dropped on the queue, entered by hand, or mirrored from an order typed directly into the ERP. Email intake is built and proven on a test mailbox.
  2. Read twice. A rules engine and a language model read it independently. When they disagree on a quantity, quantity × unit price = line total decides. Anything unreadable is held for a person.
  3. Reviewed. A person checks price, stock, tax and freight line by line. Every open problem is one line in a single list, and that list is the send button until it is empty.
  4. Sent as a draft. One click saves a draft in the ERP and runs the ERP's own approval rules on it. Clear becomes an order and is read back. Held waits for accounting. Anything that cannot be judged is held.
  5. Shipped. Pick lists are created in the ERP and appear on the warehouse's handhelds. Manifests and bills of lading are printed from the order, and the carrier's tracking number is written to the delivery.
  6. Finished. An order is finished when every line is invoiced. One merged PDF per invoice is archived: invoice, confirmation, purchase order, freight quote, pick list and signed bill of lading.

The architecture

Desk appstatic page, one per person
Local serverNode, per person, own ERP account
ERPits own approval rules decide
Shared folderthe source of truth, two ways to write
Background servicereaders, mirror, invoices, indexes
Signed updatesevery desk within the hour

The constraints decided the shape. The story of how it got there covers each choice.

Decisions that shaped it

The ERP decides; the desk never approves

The ERP's approval rules are queries stored in the ERP, and they do not run for documents created through its API. So before every send the desk reads the stored query text and runs it exactly as written. Nothing is copied into the desk's code, and when accounting changes a rule, the desk follows it without a release. A rule that cannot be read or judged holds the order.

Before go-live the approval logic ran in shadow for 30 days: 93.4% agreement, 97% on orders a day old, and every difference traced to its cause.

Draft first, and every write read back

Every send starts as a draft. Every write is read back and compared field by field, because "201 Created" is a claim. Once, a call answered 204 and changed nothing; the read-back caught it.

What the desk never does

It never changes stock. It never deletes an ERP document. It never uses the ERP's cancel, which posts a reversal; a cancelled order is closed with a remark. It never approves or rejects anything.

Prices: what a person typed, or nothing

A price a person typed goes to the ERP with its line. A line nobody priced is sent without a price, so the ERP's own pricing applies. A missing price is never read as zero. A 30-day back-test reproduced 99% of ERP-priced lines.

Who sees what is decided on the server

Every route that reads or writes an order checks the person's role and warehouses, and refuses with a reason. Hiding a button is decoration. The bug that taught this hid for a week behind admin-only proofs.

One action, one way

Two doors for people, one logic behind each, and no third way. How the rule was turned into tests.

Ship it like it matters

One release per packet: every test suite green, a signed bundle, every desk updated within the hour, mine included. A version number names one build. The release gates refuse a release that would drop an earlier one, a failing smoke test, or a reused version. "Not released" is an allowed answer, and it has been the right one.

Going live without a big-bang switch

  • The test company first, always. Production access follows people: the production list is written from the user list, never by hand.
  • Before go-live, a read-only mirror brought every open ERP order onto the desk. A new order typed in the ERP showed up as a card in about 17 seconds.
  • The rollout touched nobody's machine. Each desk's updater installed the release by itself.

Results

What Before → after
Order list at 50 desks, p95 24.7 s → 0.70 s
Opening the queue 735 → 137 ms
Opening all orders 1,054 → 122 ms
50 desks starting at once, time to first queue 11 min → 17 s
Slowest mirror query, p95 10.2 s → 26 ms
Background service time on the file share −95% and −70%
Forged cross-site requests on the shared host 546 synthetic + 47 live refused, 0 written
Damaged input files (fuzzing) 1,900 tried, 0 crashes
Order confirmation against the ERP's own print 161 text items, 503 paths, 0 differences
Whole purchase orders read exactly right 18% → 64% (held-out 17% → 62%)
Automated checks at release 13,388 in 138 suites → 29,202

My own estimates from daily use, not instrumented: 5 to 15 minutes saved per order in customer service, and about 80% of order-entry errors avoided.

What went wrong, and what it changed

  • The proofs ran only as an administrator. A supervisor's board hung for a week. Every proof now runs as each role shape. Read it
  • The theory was a slow file server. A canary said otherwise, and counters found the real cause. Read it
  • A copy lost fields. An allow-list became a deny-list backed by measurements, and the release waited. Read it
  • A status said "working" while the reader read nothing. Status now records the last call and its result.
  • Free space was read on the wrong volume. A probe now measures a machine before anything is set up on it. Read it

Where it stands

The old workstation was shut down at the end of October, and the platform's background work is moving to a dedicated machine. The plan: measure that machine with the same probe on its first day, then set it up in one go.