Insights · Build Log
I built it with AI. There is no AI in it.
At a metal fabricator in Southern California, parts come off the weld line in a trickle. An order for fifty pieces finishes as twenty on Tuesday and thirty on Thursday. Before any of them can be sold they have to travel to an outside coating vendor, and the purchase order that sends them is raised by Purchasing, who sit in an office and are not on the floor.
So the parts wait. Nobody is at fault. There is simply no moment in the working day when the floor tells the office that twenty pieces are ready to travel, and every day a finished batch sits is a day of lead time lost on a product line where lead time is the entire competitive problem.
I built the thing that closes that gap. I built it with Claude Code, and there is no AI anywhere inside it.
That is not an oversight I am admitting to. It is the most consequential decision in the design.
What I asked for
A specification already existed, written earlier in the same implementation. I did not point Claude Code at it and ask for a build. I asked for a plan.
Three constraints went in with it. Everything reads from the manufacturing system and nothing writes back, because trigger logic has to be proven correct before anything is allowed to act on it. State lives somewhere a human can open, read, and repair by hand. And errors go to me, never to the client’s Purchasing team, because an automation that cries wolf gets filtered into a folder within two weeks, and then it is worse than nothing.
I did not add a rule against putting a model in the workflow. The design came back without one, and when I looked at what the workflow actually had to decide, the reason held up.
What got built
Three scheduled workflows and an error handler.
A completion poll runs every fifteen minutes, weekdays, six in the morning to four in the afternoon. A nightly refresh caches part descriptions and works out which coated part Purchasing actually orders. A morning queue digest is built, tested, and deliberately switched off, because the physical process it depends on is not yet floor procedure, and a digest that says “nothing today” every morning trains people to ignore it.
State lives in a Google Sheet. Not because that is elegant. Because when this misbehaves eighteen months from now, somebody needs to open it, see what it believes, and correct a cell.
Here is the entire inventory of what runs. Seventeen code steps. Twelve spreadsheet operations. Twelve API calls. Eight value assignments. Four emails. Three schedule triggers. One switch, three conditionals, a loop, and a handful of no-ops.
A search across every source file for openai, anthropic, langchain, gpt, or claude returns nothing at all.
What the workflow does, forty times a day, is this. It asks the manufacturing system what finished since it last looked. It subtracts a number it already has. If the answer is greater than zero, it sends an email. That is the whole cognitive load.
The subtraction is the clever part, and it is still only subtraction. The system stores the highest completed quantity it has ever reported for each order. An order reaching twenty of fifty notifies twenty. Later at thirty five it notifies fifteen, not thirty five. Reaching done at fifty notifies the last fifteen. Purchasing always sees what is newly ready, never a running total they have to mentally subtract at eight in the morning.
The AI was everywhere else. Fifty four workflow updates. Twenty six execution inspections. Fourteen test runs against live data. Four subagents, one surveying the automation platform, one designing the node graph, one checking whether the API documentation matched the actual API. Weeks of work compressed into days.
Every judgment call the workflow depends on was made once, at design time, by a model and me arguing over live data.
What changed once I could see it
The specification’s framing implied a handful of part numbers. The live catalogue had ninety two, across seven different prefixes. The expected notification volume was wrong by an order of magnitude, which is the difference between a helpful nudge and an inbox problem.
The more useful discovery was worse. Purchasing does not order the part that comes off the weld line at all. They order the coated version, which is a separate part number with its own supplier code, connected to the unfinished one only through a bill of materials. Only twelve of the ninety two had that connection recorded in the system.
That is not a defect in the automation. It is missing recipes and parts that never go to coating, mixed together in one list, and no software can tell you which is which. So the deliverable stopped being a workflow and became a workflow plus a ninety two row spreadsheet with two blank columns and a question for the client.
This keeps happening, and it is worth naming. Automation is an unusually good instrument for measuring how complete your data is, because it is generally the first thing that has ever tried to use all of it at once.
What broke
1. The freeze that looks like calm. The loop step in the automation platform does not execute at all on empty input, which means its “finished” output never fires. Wire the position marker to advance on that output and the workflow silently freezes on every quiet poll. Then the first busy cycle after a long weekend replays four days of manufacturing orders in one burst. It reports success the entire time it is broken, which is the worst property a failure can have. The fix is a sentinel: when there is nothing to report, the step emits a single placeholder item, so the loop runs exactly once, the finished output fires, and the marker moves.
2. The tool that reported success and did nothing. Partway through, I announced that eight API steps had been switched to a different authentication method. They had not. The function that sets a parameter silently does nothing when handed a top level key, and returns success anyway. I had read the success and believed it. This is the honest counterweight to everything else in this piece: a model working through a tool it cannot fully see will confidently tell you a job is finished. The answer is not to distrust the tool. It is to verify against the live system before reporting, which is now a standing rule in that project.
3. The field name that was almost right. The manufacturing system stores configuration attributes under config_name and config_value, not name and value. Reading the obvious one returns undefined rather than an error, so on the first live run every part description wrote as an empty string. No failure, no alert, just notifications that gave Purchasing a quantity and never said what it was.
4. The spreadsheet that helpfully corrected me. Google Sheets, in its default write mode, reinterprets what you send it. An order identifier arrived as text and was stored as a number, so the string comparison on read back never matched, so every update became a new duplicate row instead. The memory that exists to prevent duplicate notifications was itself quietly duplicating. Writing raw values fixed it.
5. The prefix that looked like an answer. There is a part number prefix in the catalogue that appears to identify the coated parts. It does not. It matches some of them, misses others, and the coating vendor supplies two unrelated prefixes as well. The only reliable test is on the parent product’s default supplier. Getting this wrong would not have thrown an error. It would have put a confident, incorrect part number on a real purchase order.
There was also a paginator that would have run forever, because the header reporting whether you have reached the last page says “false” even on an empty result. That one at least fails loudly.
Four of those five are silent failures. Not one of them would have been caught by confirming that the workflow ran without errors, because in every case it ran without errors.
The part I did not expect
The most valuable decision in the whole design was a decision not to decide.
When the system cannot work out which coated part to name, it has three possible states, and the two unhappy ones are the point. If exactly one candidate matches, it names it. If none match, it says so, and tells Purchasing to confirm before raising the order. If two or more match, it lists all of them and chooses none.
There is deliberately no tie breaker. A duplicate part number in the catalogue is a data problem for the client to clean up, not a rule for the workflow to guess around, because a confident wrong part number costs a full coating run and the lead time this project exists to protect, lost twice.
Now hold that next to what a language model does when you ask it an ambiguous question. It answers. Answering is the function. You can instruct it to say “I am not sure” and it will, sometimes, and the times it does not are indistinguishable from the times it does until somebody checks.
That is the whole argument. Businesses do not break for lack of intelligence in the loop. They break because a handoff is invisible: the floor knows something the office needs, and no part of the day carries it across. Intelligence is needed exactly once, to find the invisible handoff and work out what the rule should be. After that, a model in the loop is a source of variance in a process whose entire value is doing the same thing every time.
If you are thinking about building something similar
Three questions decide whether a model belongs at run time.
Is the input unstructured language? Email bodies, meeting transcripts, and support tickets need a model, because there is no rule that parses them. A quantity from a database does not.
Does the output require judgment that genuinely changes case by case? Not judgment that was hard to work out once. Judgment that is different on Tuesday than it was on Monday, for reasons the data does not contain.
Can you write the rule down? This is the question that settles most cases. If you can write the rule down, write the rule down. It will be cheaper, it will be faster, it will run when your AI vendor has an outage, and when it is wrong it will be wrong in a way you can find and fix permanently, rather than wrong in a way you have to catch.
There is a cost argument as well, and it is not small. This workflow fires forty times a day, five days a week, roughly ten thousand times a year, with no per run fee and no rate limit to manage. But the cost argument is the weaker one. The real reason is that a manufacturing purchase order is not a place where you want a system exercising judgment. It is a place where you want a system doing exactly what it did last time.
Use the model to work out what the rule is. Then write the rule down, and let the rule run.
If you can write the rule down, write the rule down.
Where the intelligence actually belongsThe best outcome of an AI project is often a system with no AI in it, designed faster and understood better than it would otherwise have been. That is not a failure to adopt AI. That is what adopting it well looks like.
Michael DeLucia is the founder of Bantam Digital LLC, a Business Services Technology Partner in Pasadena, California, working with growing businesses on data foundations, workflow automation, and practical AI. Reach him at michael.delucia@bantam-digital.com.