Agent IAC
Treat Agents Like Infrastructure
As developer in 2026, you are probably not using one AI coding agent:
- Claude Code for personal projects
- GitHub Copilot CLI because your employer sanctions it
- maybe a third harness for its ergonomics (e.g. Pi).
Each of them wants its own instruction file, its own skills directory, its own MCP server config, its own permission model — in its own format, in its own location, evolving on its own schedule.
How to manage this heterogeneity without getting mad?
Treat agent configuration like infrastructure — canonical sources in git, deterministic compilation into whatever each individual harness expects, and drift detection on a schedule.
After five months I can say: It works for me and helps my sanity.
This article covers the approach.
The core idea
INTENT (git, human-edited) COMPILERS MATERIALIZED
┌────────────────────────────┐ ┌──────────────┐ ┌────────────────┐
│ capability config (TOML) │──────▶ │ twagent │──────▶ │ ~/.claude │
│ instruction template (J2) │ │ apply / diff │ │ ~/.copilot │
│ │ └──────────────┘ │ ~/.pi │
│ security rules (JSON) │ ┌──────────────┐ │ (skills, MCP, │
│ bash deny/ask lists │──────▶ │ twsrt │──────▶ │ instructions, │
│ │ │ generate/diff│ │ permissions) │
│ skill content (SKILL.md + │ └──────────────┘ └────────────────┘
│ self-contained scripts) │ │
└────────────────────────────┘ ▼
kernel sandbox (SRT)
memory (bkmr)
Rules:
- Never hand-edit the right-hand side.
- Only edit intent on the left, then run
apply, and verify withdiff.
If that sounds similar to Terraform, that is the point — the same reasons we stopped hand-editing servers apply to hand-editing agent configs: silent drift, unreviewable state, and no way to answer “why is this different on my other machine?”
The Dimensions
A comprehensive agent setup is not just one problem:
| Dimension | Tool | Source of truth |
|---|---|---|
| Capabilities (skills, MCP, subagents, prompts) | twagent (PyPI) | config.toml |
| Security (permissions, firewall, bash policy) | twsrt (PyPI) | .srt-settings.json + bash-rules.json |
| Kernel-level sandboxing | Anthropic’s sandbox-runtime (npm) | OS enforcement, not config |
| Memory | bkmr | SQLite (FTS+embeddings) |
| Skill content | twskls (private — 30+ personal skills, evals, review rules) |
personal repo |
| Physical distribution | dotfiles repo (private), GNU Stow + a housekeeping script | personal repo |
The private repos are private for boring reasons (employer-specific skills, personal dotfiles), but their mechanics are what this article describes, and the two compilers that do the heavy lifting are open.
Capabilities: Profiles compose intent
The heart of twagent is a registry + profiles model. Skills, MCP servers, and instruction
templates are declared once; profiles bundle them; each agent names at least one global profile:
[profiles.core]
skills = ["twmux", "skill-creator", "bkmr-memory", "tw-comprehend", ...]
[profiles.work] # employer-specific bundle
skills = ["jira-creator", "code-rationale", "tw-aws-ecs-exec", ...]
servers = ["mcp-atlassian"]
[profiles.tw-claude]
extends = ["core", "pr_review", "web_research", "java", "rust", "ideation"]
[profiles.tw-copilot]
extends = ["core", "pr_review", "web_research", "java", "rust", "work", "ideation"]
Note what this buys: Copilot CLI has employer skills and the Atlassian MCP server; Claude
Code does not. That difference is not an accident of which directory I happened to edit — it is one
word ("work") in a reviewed, versioned file.
twagent apply -G materializes it; twagent diff tells when reality disagrees.
A similar mechanism kills instruction-file duplication: CLAUDE.md, copilot-instructions.md, and
Pi’s AGENTS.md on my machine are three renders of one Jinja2 template — including harness specific quirks.
Security: Two layers
This is the dimension most personal setups skip entirely, but it deserves rigor. The design is defense in depth:
- Layer 1 (kernel):
sandbox-runtime enforces filesystem
and network restrictions at the OS level. Bash commands run by the agent physically cannot read
~/.sshor call unapproved hosts. - Layer 2 (application): each harness’s own permission model — deny rules for
Read/Write/Editon credential paths,WebFetchdomain allowlists, bash deny/ask lists.
Why two layers?
In Claude Code’s native sandbox mode — the kernel wraps each Bash command, but not the agent process, so built-in tools (Read/Write/Edit) bypass Layer 1 and Layer 2 is then the only wall between doom and me.
One can invert this by launching the whole agent inside the sandbox — which extends kernel enforcement to built-in tools and to any stdio MCP servers the agent spawns.
But it costs: the outer policy must be broader, e.g. the agent writes its own state, reaches its API, spawns MCP servers.
An harness that sandboxes its own bash commands like claude-code now nests sandboxes, which the
inner layer must weaken to survive (Claude Code ships an enableWeakerNestedSandbox setting for
exactly this case).
However, Layer 2 alone is not enough.
Agent permission rules are best-effort — twsrt’s threat model cites concrete Claude
Code issues (#6631, #24846) where built-in-tool deny rules leaked. The layers are complementary,
not redundant; twsrt compiles one canonical rule set into both, so they cannot drift apart.
Does it work in practice?
Twice during an Fable audit, my rules stopped the tooling: once
writing a .github/workflows/ file (CI files are code-execution-on-push — agents don’t get to
write them), once reading the canonical security rules. Both denials were correct. Fable was
blocked.
A secure policy feels like occasional, explainable friction. But this is a good thing!
Memory: Keep it out of the vendor
Cross-session memory is the stickiest lock-in surface an agent vendor has. My memory lives in bkmr — SQLite full-text search plus local embeddings, exposed to every harness through a skill and a passive logging hook. Switching harnesses costs me nothing memory-wise; the database is a file in a git repo.
The memory is being used by agents and myself. I have been using it for knowledge management long before agents came along. Now I reap the benefit of this approach twice.
Skills: the (rare) standard
Skills are markdown (SKILL.md) plus self-contained scripts — in my case often PEP 723 uv run --script
artifacts that carry their own dependency declarations, so a skill directory is copy-portable to
any harness that can run uv.
The Agent Skills format is a rare standard in the agentic realm, consumed by multiple harnesses; the entire skill investment survives a vendor change.
Shared code
between skills is vendored byte-identical with a make check-shared gate that fails the build on
drift — self-containment is the portability strategy, and the gate is what makes vendoring safe.
This gives me shared libraries and self-contained, copy-able skills.
Is it over-engineered?
To judge whether any of this is over-engineering, look at what exists. The community is attacking agent-config sprawl from three directions:
- Format standards. AGENTS.md gives many tools one instruction filename to read; the Agent Skills spec standardizes skill packaging; MCP standardizes how tools are served. All valuable. However, MCP is the clearest illustration of the remaining gap: the protocol is standard, but every harness still declares its servers in a differently-shaped config file in a different location.
- Vendor-native distribution. Claude Code’s plugin marketplaces distribute skills, commands, and MCP wiring cleanly — within its harness. The better a vendor’s native mechanism, the deeper the lock-in when your employer puts you in a second harness.
- Content libraries. Collections like obra/superpowers and anthropics/skills solve what your agents should know, not how it reaches different harnesses and stays consistent there.
None of that covers cross-harness deployment and drift. The nearest prior art outside the agent world: Nix home-manager or chezmoi compile a declarative source into a user environment, and GitOps made “continuously reconcile desired state against live state” the defining discipline of modern operations.
This approach is nothing more than those two ideas applied to the agent layer.
The Fable audit
In any compile-based system, materialized state is not evidence of intent. Only the source configurations is canonical.
Effectiveness
One reviewed edit reaches every harness with intent preserved. The work/personal split is a one-word profile difference instead of divergent directories. The realistic alternatives: per-harness hand-editing, or vendor marketplaces that stop at the vendor boundary .
Best Practice
Uses standards where the ecosystem is converging. Already implements the format standards and the deployment layer none of them covers.
Simplicity
Unix philosophy: small, single-purpose, composable tools. Achieved at the tool level.
Maintainability, Future-proofness
No lock-in: no vendor SDK in tooling, open formats at every boundary. Stack survives a vendor switch. However, any compiler chasing unstable targets inherits a maintenance treadmill — the same trade Terraform providers accept against cloud APIs.
Drawbacks
No free lunch!
- I am on a schema treadmill. When Claude Code removed its
MultiEdittool,twsrtneeded a release just to stop emitting rules for it. The compilers must track every harness’s format forever;diffacts as the early-warning system, but the maintenance is real. - Upfront cost is significant. Two published CLI tools, a skills repo, dotfiles integration. If I used only one harness with default settings, this would be over-engineering.
- Security rules will occasionally block you. That is the point, but it costs time and demands understanding before overriding.
What I have learned
- One canonical source per dimension — capabilities, security, skills, memory each get exactly one home in git.
- Compile, never copy. Hand-edited materialized config is drift waiting to happen.
- Make
diffa first-class verb with meaningful exit codes — then put it on a clock. Detection capability without scheduled execution found nothing for months. - Own subtrees, not entire files. Harness wstate or configuration files that “merely also hold” my config must survive my changes.
- Bet on open formats — SKILL.md, MCP, markdown instructions — keep zero LLM-SDK imports in your tooling. Model choice belongs to the harness, not the infrastructure. still delivered the two that mattered.
Closing
This is one practitioner’s setup, sample size one, and parts of it exist only because my work and personal environments force a multi-harness life.
But the underlying idea is battle tested — it is discipline we trust from Terraform and GitOps.
If a system’s correctness matters, its configuration belongs in version control, its deployment belongs to a machine, and its drift belongs to a scheduled check.
Agents are becoming the primary interface to our craft. They deserve the same discipline we learned to give other infrastructure.
The public pieces — twsrt, twagent, bkmr — are there if you want to study or reuse the mechanics.