1. The problem
Over the past year or so, as I’ve integrated agents deeper into my development workflow, something kept bothering me about them. Not the outputs, per se, but rather the structure and flow of it all - the practice. Add an endpoint and the agent picks a file layout, a client, an action, a hook, types somewhere. Add the second endpoint a week later and it picks different ones. Switch chats or models and it lands on a different approach yet again - nothing in the repo states which one is canonical.
By the fifth there are five versions of “call an upstream API and wrap the error”, and the same splintering carries into the review. Every diff checked against my memory of the other four. The sprawl wasn’t the agent’s fault because structure had become a per-task decision. Underneath it all was a shape I couldn’t see at the time.
[§1]Every conversation is essentially its own thing, and every lesson learned in there is somewhat ephemeral. What made me faster on Tuesday left little or nothing behind on Wednesday - especially across different projects or domains within projects. What I was after was a method of speccing that avoided these pitfalls.
And here I should be fair: there are ways of avoiding them. Spec-first development exists. Skills exist to carry a procedure from one session to the next. Memory files carry facts. An agents file carries context. Each one does its part, and each one is adjacent to what I ended up settling on. It’s the bringing together that’s missing, and the bringing together is the whole point, because the shape of a domain is procedure, context, reference and judgment all at the same time. They all need to be baked into the shape itself.
There was also a second problem, and it’s the more insidious one. The tools carried more, and it felt like more power at my fingertips, but when tapping into that power I kept feeling that my hands were becoming rustier. And it wasn’t just me sensing this. I heard it from other engineers too - “I could still do this by hand if I needed to, but…” That was very telling.
The more distance between the code and the coder, the more the shape of it all stops being held in hand and in head, the more the easy way becomes the default way, and soon the judgment disappears entirely from the code.
The same issues affected code review too. A new invention every single time is as difficult to review honestly as it is to develop sanely, and AI made new inventions fast. Diffs arrived faster than I could reconstruct intent, let alone read them, and their complexity and volume had increased too because the powerful tools at hand enabled reaching further. Put that all together and it’s the perfect storm.
The work quickly becomes impossible to review and impossible to hold. You go from writing every line by hand, to having autocomplete, to full agentics, to skimming, to handing decisions and review off to the AI itself - just to keep up with the pace. And in that slide you’ve lost track of where you are and what you’re doing.
Once, while implementing some server actions, I caught myself thinking, “this is repetitive. I’m going to have to do this 10 or 20 more times. Why don’t I just get the AI to do it? The shape is already there.” And then the penny dropped. It was very obvious in retrospect.
That brings me to what I’ve been doing since - which I’ve been calling stamping for lack of a better name. It moves the per-task decision to where it’s cheapest. Not a conventions document written up front, not the skill of the week, not sporadic repo facts, or memories existing on one developer’s system. Instead, a living artifact produced by the first solve of a domain. The developer decides the shape, the AI accelerates inside that decision, and the stamp carries it forward. The rest of this is the mechanics.
2. The stamp is derived, not authored
I didn’t plan it this way on purpose. The project I worked this out on I planned in a domain-shaped way almost by accident, one concern at a time, each one bounded and finished before the next. It’s the assembly-line style of planning, the thing that software product line engineering has a proper name for - and I only worked out afterwards that it’s what made the next part emerge.
Planning defines the domains of interest, which is file and code structure, components, service flows, DB tables and migrations. A developer solves each domain once, and that first solve produces the stamp. Creating the stamp is part of dev, not planning, and that ordering detail matters.
A stamp is simply an agents file that is exactly prescriptive - not generic information about the repo. The file patterns to create, plus references into the first implementation as the thing to extend. Not a rough suggestion or approximation. A stamp for a service call names the files involved and points at the working code. The prose routes you to the code. It doesn’t substitute for it.
Why AGENTS.md and not CLAUDE.md, .cursorrules, or copilot-instructions? They all do more or less the same job, but keeping three copies in step is three surfaces to drift. AGENTS.md[1] describes itself as a simple, open format for guiding coding agents, and it’s the one shared standard rather than belonging to any one vendor. That’s just my preference.
Why not another established artifact, like a skill? Skills and commands live at the top level, at the project space or user space, not directly adjacent to what they describe. Agents files, however, can be nested and can collocate with what they describe. This collocation prevents the pointer and the code from drifting apart. The other option - an agents file pointed at a dedicated skill - would be two artifacts doing one job and again opportunity for drift.
What isn’t preference is what the format is for: guidance. And a lot of the agents files I’ve encountered have been suggestive at best, generic at worst. They name the framework - which the model already worked out from package.json before it got to the end of that line, the architecture - which it immediately recognized the pattern of, and then they stop, or cobble together sporadic facts about the codebase learned after they matter. But boil it down, and an agents file is a perfect home for everything a stamp represents. The file format is simply the vehicle, not the idea.
[§2]Every subsequent implementation of a stamp is a stamping event. The agent fits a few dynamic details to the known shape. Mechanical work with inference gluing together the seams, but no reinvention from scratch - that’s the entire trick.
And then the part that took me a while to see: with stamps, implementation and review are the same instrument used at two altitudes. The event produces a domain implementation from the stamp’s shape and then the review checks that implementation against the same shape. This helps dissolve the expensive invention that arises otherwise.
Did the files land where the stamp says? Are they carrying the contracts it names? The stamps bound diffs within predetermined, deliberate shapes. The heavy lifting of review becomes largely a conformance check, instead of fresh archaeology every time - freeing mental capacity to focus on what’s actually changed rather than fighting invention to keep the code, and yourself, sane. It’s the same shape, over and over, and that is exactly the kind of work a GPU can accelerate effectively.
The parallel to a series of related tickets in traditional dev isn’t accidental. In both cases the implementer knows roughly what they’re aiming at, and the reviewer knows roughly what to expect. In traditional dev the expertise that makes that flow effective is held in the team’s heads. The difference here is that the shape - and the expertise behind it - lives in the repo, as a stamp.
The shape is the value, against my instinct to compress. Compressed files are harder to grep, diff, and rewrite in isolation, and those are the operations stamping depends on. If AI acceleration is the brush, the stamp is the composition, so speed lands in one picture instead of a hundred sketches. WET is a smell on a human team, but with an agent holding the brush it seems like the right trade-off. Duplication costs humans, an agent just re-reads in an instant. The repeated shape keeps each change on the rails.
3. Solving a domain once
A stamp is only worth making if it covers repetition - so a domain I’ll touch once gets no stamp. The first stamp is expensive because the decisions are being made for the first time, and that’s the good part - the judgment to amortize - not the overhead to shave. Roughly a hundred ad-hoc tasks collapsed into five or ten solved domains. Every stamping event thereafter is cheaper, because the decisions are already made and the stamp names where. An app where every domain is genuinely unique isn’t a fit - there’s nothing to amortize. That’s a prototype or a research project. This method is for structured repetition.
Within a domain the solve order is primitives, then composites, then one slice, end-to-end through every layer. The first working pass gets stamped. In the frontend domain that slice is a screen or component, and later implementations are assembly plus whatever the design adds, because the screen implementation pattern was stamped by the first solved screen. The mechanics and exact outputs differ per domain and per implementation, but the ordering and benefit don’t.
Which brings me back to those server actions I was implementing. I was in a branch doing them by hand with an agent on the side, and I’d already worked out what the shape needed to be. So I wrote a prompt that said, in effect, here’s the shape this domain needs, here are the reference files, fill in what has to change between implementations, and don’t deviate beyond what’s absolutely necessary.
I fired it at one area first to try it out, and it came back well, with very few adjustments, having saved hours. I fed the adjustments back into the shape and the rest of the areas went out as 297 files in a single PR - which I’ll admit was ever so slightly excessive on my part. I got carried away with the power at my fingertips. But every file fitted inside the same shape, so the review was checking against the shape, not reconstructing intent - the only reason a PR of that size was manageable at all.
Not best practice by any means, and in hindsight I’d have split it up by area, but an hour or two of work for what I’d have otherwise budgeted days for.
4. Documentation wears out. Stamps wear in.
Documentation drifts, and everyone has watched it happen. You write it down, the code moves on, and a few months later an agent is following a lie with total confidence. I expected stamps to do exactly the same thing - and left alone, they would. But a stamp isn’t left alone. Documentation gets read. A stamp gets used.
What keeps them in sync is use, and it’s the part I didn’t design or expect - it emerged from use itself. A stamp is read every time it’s used, and every use is a check against the code and the resulting output. That’s the point after all - you can’t check a diff for conformance without reading the thing you’re conforming to. Stop using it, and it’s just documentation again.
As an explicit mitigation I’ve also started dating stamps - a last-verified marker, which is the cheap half.
The expensive half is noticing which domain shifted and enforcing the correction, and that still comes down to attention. That’s the weakest joint, and it’s also the one that resolves itself more than I expected.
Every stamping event reads the stamp and outputs the code, and the input versus the output is where drift shows up, so the thing that makes the stamp useful is the same thing that keeps it true. What I’d still want is an explicit trigger that fails loudly rather than a convention that hopes to be noticed, and the closest signal I have is a stamping event that runs longer than the stamp says, or produces files the stamp doesn’t name.
But notice, the same strictness that makes review mechanical is what makes drift obvious by construction. It also moves review inboard. The making, checking, and correcting of the stamp is itself review, done while the code is warm - not cold.
The correction loop is cheap too, and that’s the whole point of the metaphor. A stamping operation is cheap. You dip it in ink and press it down, and you can adjust a stamp and throw away an incorrectly stamped sheet of paper very easily. The first stamp is a hypothesis, not a verdict - a freshly inked stamp presses faint and uneven, and shows where the ink hasn’t taken. Wearing out happens between uses. Wearing in happens through them.
So the flow is simple. Stamp → notice the gap at the next stamping event → fix the stamp → restamp. All the while the domain understanding settles deeper into your head, the thing that used to happen by default when you wrote all the code yourself. It also becomes more battle hardened. The gap isn’t predicted. Repetition exposes it, and the gaps shrink over time. An incorrect stamp is a small fix, because the shape already exists and fixing it means editing one document and one implementation.
The simplest example of this loop I have is a change I needed to make to add request validation. I’d forgotten to add Zod validation on the incoming requests. It wasn’t in the first implementation, so it wasn’t in the stamp, and it even slipped through review - the gap was in the shape itself, and review checks against the shape.
[§4]Misses are routine. What isn’t routine is that the catch came from consistency, not model cleverness - another stamp already had what this one was missing, and the difference is what surfaced it. It came out while the agent was stamping some AI SDK tools. The tools allow input schemas by design, and in this case they were calling the same code the server actions were. So the missing validation was the one inconsistency. A structural catch, mid-implementation, not by explicitly looking for it.
I worked out a clean way to fix it, added it to the stamp, and then told the agent to apply it to every action that already existed. A few lines of change per action, plus one new file each. It went out in one shot, with minimal deviations from the shape, and review was quick because it was clear what to look for. A cheap restamp of the whole domain.
Gaps turn up in a few ways, and all of them via use. The small ones are made evident by the stamp itself. As I added server action after server action, little things turned up that needed plugging, and each plug went into the stamp, so the next one came out cleaner than the last. After just a few iterations the leverage compounded. Review highlights gaps too - because it’s a form of use. Then a while later, during an implementation in a different domain, that Zod gap I mentioned popped out from another stamp entirely, and it was an architectural gap rather than a slip in a single file.
That’s what allows stamps to be somewhat self-healing - and I didn’t design anything that way. Any single stamp is itself checked through repetitive usage and tiny resulting gaps. The whole set of stamps also checks itself, because the stamps are cut from the same shapes and share or circle the same references - they don’t exist in isolation. So using one is also a test of the others. The noticing comes nearly free that way, arising from the stamps themselves. The deciding is still mine, which is the part I want. It’s also the closest thing I have to the loud trigger I said I wanted.
Wearing out happens between uses. Wearing in happens through them.
5. Brownfield: the retrofit sequence
Technically everything up to now has been described in the context of greenfield projects where you can plan and develop cleanly up front. It definitely makes things easier. Brownfield is the part I have least figured out. There’s usually no canonical first solve to derive from - only candidates. The sequence I’d run is four steps, archaeology → canonical instance → forcing function → conversion by touch.
[§5]Archaeology is usually an AI-assisted census of those candidates. Tools like graphify[2] and GitNexus[3] turn it from a grep expedition into a query, though they only list candidates. Choosing the one that becomes the reference is still by hand. The choice plays the role of the first solve - the stamp derives from it the same way it would in the greenfield approach, just with the reference chosen instead of solved. The forcing function is what decides whether any of it works, and it’s simple: a candidate conforms to the stamp or it records why it doesn’t. Without that, the stamp is just documentation with ambition.
Conversion doesn’t happen in one clean sweep. Multiple candidates exist, and each needs to be converted as it’s touched. Each touch resolves one of three ways: list it as an exception, take its difference into the shape, or convert it to the shape. An exception gets listed in the stamp it deviates from. An adjustment edits that stamp. Both are changes to the same agents file. The third way is just a mini stamping event of its own - restamp the candidate so that it conforms. This is the triage process, and it’s what keeps a stamp the best known version of its shape.
The curve is slow-in, slow-middle, fast-out, and the middle is where I imagine these efforts would die. Attention is the filter, because conversion happens when code gets touched, so a domain nobody visits, for one reason or another, never conforms. The restamp loop matters here too, maybe most of all - until a stamp exists the candidates keep accumulating. Brownfield is slow under every approach. I’d rather that slowness buy a shape that accumulates speed later, with the acceleration recorded rather than reinvented and the shape saved rather than splintered.
6. What it doesn’t fix
No first solve means no stamp. A domain that is never solved even once, even badly, has nothing to derive from. Greenfield works by clean planning and development to arrive at the first solve. Brownfield works around it by choosing a candidate as the first solve. None of that means the method is the right fit for every case and every project.
And none of this is autonomy. A stamp doesn’t make the work safe to run unsupervised. It makes supervision cheap enough to run at scale. The speed comes from care and the work being watchable, not from nobody caring or watching.
The load doesn’t disappear - it moves. Structural review came inboard, but absolute proof of correctness remains with the tests. A stamp can tell you the shape held, but it can’t tell you the thing actually works.
Another part I haven’t solved is domain granularity. I’ve experimented with up to nine domains. Splitting the domains too granularly means many stamps, each with little mileage. Too coarse leads to either a stamp too large to review, or too vague to help. I have no formula and no neat answer, only a feel for it and the knowledge that a domain should cover enough repetition. I make no claim that nine is right for anyone else, or any other project - or indeed that this method is. It’s a practice that’s worked for me, and that’s the whole claim.
[§6]7. Where this could go
At this point I’ve built more than one app using this approach. The next logical step that comes to mind is to raise the stamp’s coverage, so the same move can go a level up. If the app shape is the top-level stamp, the next app (of that shape) starts from it, and its surfaces are stamping events. That’s a lot - and it rubs up against the unsolved domain granularity problem that I just mentioned. I haven’t run that yet because it’s not my focus right now, but I am curious what’s possible here.
8. Where the judgment lives
The standard phrase with agent usage is human in the loop. The agent drives, a person supervises. In practice the supervising eventually devolves into prompt-hacking where reach often exceeds grasp. A click from someone, often without full context or understanding to judge what they’re approving, spread across several simultaneous task streams, and the problem re-described until the output looks right. By the end of a session your head feels muddy and you’re less sure of the system than when you sat down.
[§8]It’s what happens when the volume and capability of invention outruns the time you have to judge and understand it. A loop is only valuable when the human can tell whether the output is wrong. Stamping runs the other way.
That detail is important. None of this works well if the first solve is something a developer couldn’t largely implement and review without AI. It would still produce output, of course, the same way unguided prompting does, but the structure would go back to being a per-task decision. A per-task decision is not an agreement. Nothing gets settled that the next run, or the reviewer, can be held to.
AI can be in the room for the first solve, absolutely. Pen-testing a design, exploring options, arguing the other side. There’s great value there. What it isn’t doing is inventing the baseline structure and making the judgments. That understanding is quiet infrastructure, because review only works if the reviewer knows what conformance means, and that model is what keeps fast, high volume review sane.
Everything after runs inside the stamp, where that judgment lives. The file shapes, the error contract, the boundaries. A deviation is an opportunity for correction and understanding (and often an important architectural one). The stamp is just the artifact after all, and the design chunk is what it carries. Either way the gap is noticed while it’s still cheap.
Traditional development comes first, with agents in the loop. From then on, the human is in the loop of everything, for free, encoded as the stamp and improvements to it. That’s also what answers the rust. The judgment never leaves, so the dev stays the main instrument in the solution. It’s muscle memory for AI and for developers - without losing the speed benefits of AI. Learn to ride the bike once, then ride the bike. You don’t start from scratch every single time you get on.
Underneath it all the shape is a training loop - forward, error, update. The acceleration of effort belongs to the GPU, and the stored judgment belongs to the developer. The combination is what maintains agency.