A drainage study handed me a list: chainage, ground elevation, and type, for about 28 crossings along a ring road we were designing around a city. From that list, 26 relief culverts had to be placed inside our road-design software, each one cutting across the road, each end sitting at the real toe of the embankment. (A larger box culvert at a river arm made 27 structures in total; that one was already placed by hand.)
This is the unglamorous middle of an engineering project: repetitive, geometry-heavy, easy to get subtly wrong. I had an AI agent write a script to do it, and once the script worked, I had it generalized into a reusable skill. The automation isn’t the interesting part. The interesting part is the one detail that decided whether the output was usable or quietly wrong, and the fact that it was hiding in a different file than I first looked in.
The agent has a name. I call it MARCO, and it’s worth being precise about what that is, because “agent” carries more mystique than the thing deserves. Picture less a standalone autonomous AI and more a general-purpose model pointed at a specialized setup: a knowledge base of the road-design software, the rules our projects follow, and a handful of small skills it can run. The intelligence is general; the context and the tooling around it are what make it MARCO. So “I gave it to an agent” really means I gave the model the right knowledge and let it write and run the script.
What the task actually is
A culvert needs three things to be placed: its station along the road, its invert elevation, and the two points where its ends reach the toe of the slope on each side. That last one is the whole game. Get the toe wrong and the mouth ends up buried inside the fill, or floating past where the ground actually is. The kind of wrong that looks fine in a thumbnail and bites you on site.
The easy answer, which is wrong
The shortcut is to treat the toe as a fixed distance from the centerline. One offset, applied to all of them. It’s wrong almost everywhere. The toe moves with every cross-section: taller fill pushes it out, a steeper slope pulls it in, superelevation in a curve makes the two sides different. Across these 26 culverts the toe-to-toe width ran from about 19.5 to 27 meters, and in curves the left and right sides didn’t match. A constant offset would be off by meters exactly where it matters.
The real toe was in a different file
Here’s the part I got wrong at first. I went looking for the toe in the obvious file, the one with the alignment, the grade, the superelevation. It wasn’t there. That file describes the road; it doesn’t pin the toe at each station.
The real toe lives in the cross-section file, tagged with a point code, and only after the software has actually computed every cross-section. There was no clever method to it. I just had to know which file held the ground truth. The script reads that file, finds the toe code on each side, and because the culverts fall between computed sections, it interpolates between the two neighboring profiles. The mouth goes to the higher side; its level comes from the toe in that file, not from the ground elevation in the original list.
I validated it before trusting it
There was already one culvert placed by hand, about 28.6 meters wide. Before generating the other 26, I used it as a test: compute its ends from the cross-section file and check them against the real ones. Cheap insurance against a confident, wrong batch.
The trap that silently breaks everything
The output is the software’s native format, and it has a nasty edge. The header carries a fixed set of object slots and a counter that has to equal the real number of records. Get the counter wrong and the software loads the structures empty. No error. Just nothing where 27 culverts should be. That’s the kind of brittle, undocumented detail that eats an afternoon the first time and shouldn’t cost you a second one.
The lesson that travels
When an agent gets something like this wrong, the cause is almost always the one I hit here: it’s reading a convenient stand-in instead of the real thing. A fixed offset, the obvious file, an assumption nobody wrote down. The model was never the hard part. The hard part was making sure it read the real toe of the slope and not a number that was easy to type.
So this is what I check now before pointing an agent at anything: what’s the real source of truth here, and is the agent reading it, or reading something near it? Usually the ground truth already exists somewhere in the system. The work is wiring the agent to it instead of to the shortcut.
Then freeze it
I didn’t start with a polished tool. I started with a script hardcoded to this one project, got it right, checked it against the hand-placed culvert, and the same afternoon generalized it into a skill: parameterized, reusable, carrying the details that were expensive to find. Use the cross-section file, not the alignment file. Use the toe code. Fix the header counter.
The one-off solved one project. The skill solves every one after it. That second step is the one I used to skip, and it’s where the compounding lives.
What I kept
The agent didn’t need to be smart about civil engineering. It needed two unglamorous things: a pointer to the real source of truth, and an answer turned into something reusable. The culverts shipped. The recipe is the part I kept, and it’s already waiting for the next project.