sysid blog

Agent IAC

Treat Agents Like Infrastructure

As developer in 2026, you are probably not using one AI coding agent:

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:

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:

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:

  1. 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.
  2. 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.
  3. 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!

  1. I am on a schema treadmill. When Claude Code removed its MultiEdit tool, twsrt needed a release just to stop emitting rules for it. The compilers must track every harness’s format forever; diff acts as the early-warning system, but the maintenance is real.
  2. 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.
  3. Security rules will occasionally block you. That is the point, but it costs time and demands understanding before overriding.

What I have learned

  1. One canonical source per dimension — capabilities, security, skills, memory each get exactly one home in git.
  2. Compile, never copy. Hand-edited materialized config is drift waiting to happen.
  3. Make diff a first-class verb with meaningful exit codes — then put it on a clock. Detection capability without scheduled execution found nothing for months.
  4. Own subtrees, not entire files. Harness wstate or configuration files that “merely also hold” my config must survive my changes.
  5. 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.

#Ai #Development #Agents