Core, Adapter, Content – Why I Took My Agent Workflow Apart

My agent team worked beautifully – in exactly one repository. Why moving it into a second project failed, which dividing line I learned from it, and why I still owe the proof that any of it is generic.

09/27/2026

•8 min read

Note on the Content

This series describes my insights from using AI in the software development process. These are personal experiences and conclusions from which I derived measures that worked for my projects. The series does not claim to be a comprehensive guide or universally valid — it is meant as inspiration for your own projects.

In the agentic coding series I described how I turned a single prompt into a team of specialised agents: business analyst, solution architect, realisation plan, developer, test agent. It worked. It worked in exactly one repository, though.

This post starts on the day I tried to take that team into a second project.

The Move That Never Happened

The plan was unremarkable: copy .claude/, start Claude Code, keep working. What actually happened was more interesting — and obvious in hindsight.

The solution architect cheerfully lectured about directories that did not exist in the new project. The developer agent insisted on a codegen step the new stack has never heard of. The test agent demanded a framework that was not installed. And the most interesting case: an agent I had adopted from another setup had never been runnable in any of my projects — it carried the assumptions of a foreign stack, and nobody had noticed, because it never produced a hard error. It simply answered plausibly.

That is the failure mode that has occupied me ever since: an agent that finds no specification does not stop. It improvises. And improvised output looks exactly like correct output.

The obvious reaction would have been to rewrite the agents for the new project. That is exactly what I did first, and after the second project it was clear that this way I am not maintaining three agent sets — I am maintaining three sets that slowly drift apart.

The Question Was Not "Which Stack", It Was "Which Line"

The breakthrough came from an uncomfortable exercise. I went through every agent file line by line and asked two questions.

  1. Would this line be equally true in a FastAPI service?
  2. If not — is it knowledge about the stack or knowledge about the process?

The result surprised me. By far the largest part of an agent file is process knowledge: in which order do I work, what do I read first, to whom do I hand the result, when do I escalate. None of that depends on whether Dart, Python or TypeScript sits underneath.

Stack-bound content is surprisingly little — but it is scattered everywhere. A command here, a directory path there, a naming convention in a subordinate clause. That is exactly why the file was not portable: not because of its size, but because the stack knowledge was stirred into the process knowledge.

Three Layers

Out of that grew a separation that is now the directory structure of the repo — not just an idea in my head:

Diagramm wird geladen …
The core reads the adapter, the adapter describes the project. Only the middle layer is rewritten per stack.
LayerWhat lives thereWhen porting
A — coreagents, skills, core rules, guards, templatescopy unchanged
B — adaptergate commands, stack conventions, test strategy, architecture rulesrewrite per stack (~1 day)
C — contentUCs, concepts, plans, findings, agent memoryemerges in the target project

The decisive property is in the direction of the arrow: the core reads the adapter, never the other way round. A core agent references project-context.md — and it does not care whether that file says Flutter, FastAPI or Nuxt. The filename is stable, the content is variable.

That sounds trivial. It is, however, the difference between "I have sorted my agents nicely" and "I can move them", because it turns a convention into an interface.

The Rule That Keeps the Core Clean

A single rule prevents the layers from merging again:

An agent only enters the core if it would run unchanged in the target stack.

That is the direct lesson from the adopted agent that never ran. Everything else belongs in the adapter rules or in an optional module — that is how my UI module came about: three agents that presuppose a design system and a component catalogue. In a backend project they have no business being there, so they are not core, they are an option.

The rule is uncomfortable because it often decides against the short path. When an agent could do one small thing better in the current project by being handed a path, that is always tempting. The price is that this very agent will be the one improvising plausibly during the next move.

What Layer B Actually Contains

The adapter is not a config file with a few variables. It is five rule documents plus a script, and they answer the questions an agent would otherwise guess:

Adapter fileAnswers
project-context.mdWhich stack, which layers, which hard rules, which reference module?
coding-guidelines.mdWhat does code look like here — conventions, canonical patterns, anti-patterns?
quality-gates.mdWhat has to be green, what is deliberately weakened, and until when?
testing-strategy.mdWhich test levels exist, with what tooling, and how are they run?
styling-guidelines.mdUI only: design tokens, component catalogue, forbidden patterns.
scripts/quality-gates.shThe actual commands — the real swap point per stack.

Two things in there were a change of mind for me.

First: the gates script is the most important adapter, not the prose. All five rule files describe something; the script does something. And because it is invoked identically by the local session, the git hook and CI, a gate cannot possibly be defined green locally and red in CI. More on that in the next post.

Second: the reference module is a mandatory field. The adapter names a module that lives out the architecture rules — the agents read it before they build. That is the difference between "follow clean architecture" and "this is what it looks like here". In a fresh project that is easy. In a grown codebase it is the most awkward question of the entire setup, and the honest answer is often: there is none. Then you name the least bad module and list its deviations explicitly. That is honest, but it is no substitute for a clean example.

The Preconditions That Have Nothing to Do With Technology

While taking things apart I noticed that the workflow generalises over technology but makes tacit assumptions about context. Those now sit as a table at the front of the documentation, because otherwise they surface exactly when it is too late:

  • The gates have to be cheap. The rhythm "one commit per plan step, gates after every step" presupposes runtimes in minutes. With expensive codegen the economics tip over, and then you need a fast profile per step and the full profile at the end.
  • One person holds all three approvals. Requirement, concept, merge — in my case that is me. In a team those gates have to be assigned to roles, and the workflow does not enforce the separation.
  • The repo host needs a PR gate. Without branch restrictions on the default branch there is no technical enforcement of the merge gate — and then discipline is carrying it again.

These are not details. Anyone adopting the workflow without those preconditions gets the structure but not its effect.

The Honest Part: The Proof of Genericity Is Missing

And now the paragraph I would most have liked to leave out.

The workflow grew and hardened in one productive repository. A port into a project with the same stack proves precisely nothing — it proves that copying works. The hidden assumptions only show up in a stack-foreign move, and that is still outstanding. Until then the installation procedure and the brownfield path are well-considered but untested routes as well.

On top of that comes a metrics trap I walked into myself. To measure how generic the core is, I counted stack mentions per file — how often do "Flutter", "Dart", "pub" appear in a core file? The number went down nicely. Except it measures vocabulary, not coupling. A file can be phrased in a perfectly stack-neutral way and still presuppose a directory structure that does not exist elsewhere. As a worklist the measurement was useful; as proof it is worthless.

The only real test is a deliberately hostile one: a stack I do not like, a mini feature, once all the way through the pipeline. Until I have done that, "technology-generic" is a hypothesis with good reasoning — no more.

Conclusion

What I learned is really an old insight applied to new material: reusability does not come from something being good, it comes from the dependencies pointing in one direction. My agents were cleanly cut from the start — but they were attached to the project in every direction, and that is why they could not be extracted.

The dividing line I take away: process knowledge into the core, stack knowledge into the adapter, project knowledge into the project. And when I am unsure where something belongs, the test question is not "where does it fit best" but "what happens if this piece of information is missing in the next project".

The next post is about exactly that: how do you turn this ordering into a contract that is checked by a machine — instead of a convention everyone is supposed to follow?

How do you handle your agent setup across projects? Do you copy and adapt it, deliberately maintain several versions — or have you found a dividing line that holds up? I would genuinely like to know.