DEV Community

Cover image for I was solving agent portability at the wrong boundary
Michael Truong
Michael Truong

Posted on

I was solving agent portability at the wrong boundary

Copying the last repo's agent setup into a new one worked at first. It also copied product-specific assumptions. Once I had several active projects, there was no longer a single canonical repo I could copy from. Every improvement now had several places it could drift.

I was doing that across Codenames AI, a portfolio site, and a resume generator. Shared workflows for planning, editorial work, dependency upgrades, review, and repo bootstrap had accumulated around them. Some still lived inside product repos simply because that was where they had evolved.

I wanted the next repo to start with the methodology already available, without cloning the implementation details of the last product.

My first instinct was to solve that with an MCP-shaped architecture. Extract the shared behavior into a separate repository. Expose it through a remote tool boundary. Every repo could call the same capability when it needed merge-safe planning rules or editorial workflow guidance.

That felt rigorous. One service. One contract. One place to version policy.

Why the MCP-shaped instinct looked right

The idea lived in notes and conversation: treat portable agent policy the way you would treat a remote tool.

Building it would have bought a clean contract. It would also have attached the baggage that belongs to real tools:

  • a runtime or service boundary
  • an MCP contract and deployment story
  • versioning and invocation decisions
  • "when do we call this?" routing inside every agent session

That overhead makes sense when the capability is genuinely external: query Notion, pull PostHog metrics, deploy through Vercel. It does not make sense when the capability is mostly operating methodology: how to slice plans, when to stop after opening a PR, how to keep merge-safe invariants explicit.

Planning standards and merge-safe workflows are agent policy and procedure. They are not remote resources waiting behind a tool boundary.

I never built that service. I did not need to.

What shipped instead: four durable scopes

The decomposition was the work: what should follow me into every repo, what should stay behind a tool boundary, what should stay with me as procedure, and what must live in the repo itself. I happened to implement that split in Cursor (user-level rules, MCP config, installable skills, repo files). The architecture is the scopes:

Scope Role What belongs here
Always-on policy Invariants in every repo Short rules: execution authority, repository topology, stop-after-open, tool preferences, pointer to planning methodology.
Shared tools External tool boundaries Notion, PostHog, Vercel after you authenticate the service.
Reusable procedures Workflow installation Full portable skills such as staged planning and new-repo bootstrap.
Repo files What must live in the repo Stable mechanics (local hooks, remote environment lifecycle, CI) and product knowledge (AGENTS.md, domain rules, product skills, review guides)

Always-on policy kept a pointer to the planning methodology instead of a second copy of it. Copying one product repo's harness into another just to match would have recreated the drift.

Shared tools stay behind that authenticated service boundary.

Reusable procedures stay at user level. The new repo does not store those skills. I run bootstrap once to write stable mechanics into repo files. That step does not copy the skill into the repo, and it does not write product knowledge because it varies by product.

The scopes also have different update semantics: policy changes flow across existing repos, while bootstrap changes become the baseline for new ones unless I explicitly migrate older repos.

The interview showed what still had to be reconstructed

The first serious cold-start test was a timed AI-native product-build interview. I used the same scopes in a genuinely new repo under time pressure.

The interview proved the methodology did not depend on my existing repos. It also showed that too much generic setup still had to be reconstructed in an empty one. The agent put instructions in the README instead of AGENTS.md. Hooks that should have wired the remote agent environment were not reliably set up. Setup that was obvious in my established repos was not obvious when an agent had to invent it under time pressure.

Policy, procedures, and shared tools were already available. What failed was leaving stable repo mechanics to be rediscovered. An empty repo still has to run those hooks and that remote environment. The following week I converted more of that baseline into deterministic bootstrap: known-good scripts and templates for local hooks, remote environment lifecycle, CI, and other baseline infrastructure, not another round of agent redesign.

The goal is not zero bootstrap. It is to stop spending agent reasoning on decisions I have already made.

Takeaway: Splitting the problem by ownership and lifecycle showed I did not need an MCP-shaped architecture. Policy could stay always-on, procedures could stay at user level, stable mechanics could be materialized by running bootstrap, and the agent could spend its reasoning on the product.

Top comments (15)

Collapse
 
reidmarlow profile image
Reid Marlow

This boundary feels right to me. Repo-local agent rules are good for product facts, but planning habits want the same treatment as editorconfig or a shared CI template. Copyable, inspectable, and boring enough that a new repo can disagree without deploying a service.

Collapse
 
michaeltruong profile image
Michael Truong

I like the shared CI analogy. The ability for a repo to deliberately disagree is an important part of the boundary too. The shared layer gives me the default methodology, while repo-local state stays explicit when the product genuinely needs something different. That feels much healthier than copying the whole harness and slowly losing track of which differences were intentional.

Collapse
 
rulestack profile image
Rulestack

We drew the same line between shared tools and reusable procedures, with one boundary placed differently: our procedures load only when a task matches them, never always-on, on the assumption that anything present on every turn is also paid for on turns that do not need it. The README-instead-of-AGENTS.md slip is the part I recognise — our fix was not a better prompt either, but a commit-gate test that fails when the instructions point at a file that is not there. Bootstrap ordering is the piece I have not solved: whatever runs first sits outside the thing it configures, so it gets trusted rather than checked.

Collapse
 
michaeltruong profile image
Michael Truong

I agree with the task-matched loading point too. That was part of what made the MCP-shaped approach feel increasingly wrong for this. I would have been exposing a shared tool surface broadly when much of the methodology only needs to exist for the repos or tasks that use it.

I suspect my current plugin is still a coarser packaging boundary than the eventual one. It could decompose into skills that are enabled only where they are useful. Bootstrap can disappear once it has materialized the repo mechanics, while something like planning can remain as a thin reusable procedure.

That last point is interesting to me too. I ended up at almost the same place after the cold-start failure. If bootstrap is responsible for establishing an invariant, checking for it afterwards is weaker than making the bootstrap deterministic enough that the invariant is true by construction. I like your commit-gate example too. It turns a missing instruction from something the agent has to notice into something the repo can reject.

Collapse
 
leftoverpzero profile image
Leftover

I hit this on PZERO too. The client is portable. Leftover daily capacity is not. It dies at UTC midnight.

I do not keep leftover offers loaded like always-on tools. I quote the live row when I have a job. Thin book, I shrink the job. Same client in a new repo does not mean the capacity is still there.

Collapse
 
skillselion profile image
Skillselion

"Planning standards and merge-safe workflows are agent policy and procedure. They are not remote resources waiting behind a tool boundary." That line earns the whole post. We watch a large catalog of agent skills daily, and the ecosystem is converging on exactly your four-scope split: thin always-on rules, external MCP for genuinely external systems, installable skills for procedures, and repo files for product truth.

Your "several places it could drift" problem has one more level though: once staged-planning ships as an installable skill across repos, each installed copy ages independently, and you are back to no canonical source one step down. Curious how you handle that: reinstall on a cadence, pin to a tag, or treat the user-level copy as the only source and never vendor it into repos?

Collapse
 
michaeltruong profile image
Michael Truong

Good catch. I think there are a few different lifecycles hiding inside "installed copy."

Today, things like /planning-methodology and /repo-bootstrap stay at user/plugin scope. The always-on rules point at the procedure rather than vendoring it into every repo, so there isn't an independently ageing planning copy in each repo. When the procedure changes, I version the plugin and merge to the marketplace repo. With the marketplace connected, that propagates to my IDE and Cloud Agents without vendoring a copy into each product repo.

Bootstrap is different. It materializes stable repo mechanics like hooks, CI, a minimal AGENTS.md, and environment lifecycle, then stops. Those are snapshots by design. Existing repos only pick up later changes when I explicitly migrate them. Product knowledge and product-specific skills can evolve in-repo afterward, and that divergence is intentional.

The always-on policy has different semantics again. I deliberately keep it thin and separate from the plugin, so plugin procedure updates propagate through the marketplace while policy updates only propagate when I refresh the User Rules.

There's also a fourth lifecycle I haven't really formalized yet. If something needs to stay centrally defined, shouldn't be locally modified, and needs controlled upgrades, I'd probably treat it like a normal versioned dependency. Pin it, then let the repo owner choose the upgrade cadence, potentially with Renovate-style automation. Some of the mechanics I materialize today are an awkward middle case because they're snapshots rather than a live subscription.

So the scope split isn't just about where something lives. It also determines how changes propagate: plugin procedures from the marketplace upstream, policy when I refresh User Rules, repo mechanics through bootstrap or explicit migration, and potentially shared dependencies through normal version upgrades.

Collapse
 
xulingfeng profile image
xulingfeng

The “wrong boundary” realization really resonated with me.​
A really good reflection, and I’m glad you wrote it down and shared it. These are the kinds of lessons that are easy to overlook when you’re in the middle of building things.👍️

Collapse
 
michaeltruong profile image
Michael Truong

Thanks, really appreciate that. This one took a while to get to the actual lesson. I kept thinking the problem was “how do I centralize this?”, when it was really “which things should propagate, which should be installed once, and which should live with the repo.” Glad the “wrong boundary” framing resonated.

Collapse
 
xulingfeng profile image
xulingfeng

Sometimes a change in perspective makes everything click. That moment when you finally see the real problem is pretty satisfying. 👊

Collapse
 
deanlee profile image
Dean Lee

This maps to a risk I keep seeing with agent setups. The expensive boundary is not where the tool call lives. It is where policy changes propagate without dragging along old product assumptions. Treating bootstrap as deterministic repo mechanics, and methodology as user-level procedure, seems like the right split.

Collapse
 
michaeltruong profile image
Michael Truong

Exactly. I think the propagation behavior is the part I was underweighting at first. Once I separated “should update everywhere” from “should be materialized into the repo once,” the ownership boundaries became much easier to reason about. Glad that distinction landed.

Collapse
 
topstar_ai profile image
Luis Cruz

Your approach to identifying and separating the different scopes within your architecture is insightful, especially the emphasis on treating agent policies as internal methodologies rather than external tools. This distinction can greatly reduce complexity and enhance maintainability across your projects. Have you considered implementing automated checks to enforce the boundaries you've established? If you're looking for help in refining these scopes or further developing the architecture, I'd be happy to discuss a paid collaboration.

Collapse
 
michaeltruong profile image
Michael Truong

Yes. That became the next step after separating the scopes. I’m moving stable repo mechanics toward deterministic bootstrap and checks rather than relying on the agent to remember or reinvent the boundary. The interview was useful here because it exposed exactly where instructions alone weren’t enough.

Collapse
 
kgaidev profile image
kgaidev

Every one of the four scopes holds something a person authored. There's a class of knowledge the agent produces while working in a repo, and it's the class that goes missing: we tried X here and dropped it because Y. It isn't policy, it isn't a procedure, and it's too product-specific to leave the repo.

Your update semantics line is where it gets interesting, because this class has a third one. It doesn't flow across existing repos and it isn't a baseline for new ones. It gets written once, in the repo where it happened, and the next person into that code has to read it whether they're on Claude Code or Cursor, so it can't sit in either tool's config format.