Agent architecture

SOLID was waiting for agents.

The old object principles make more sense when the changing parts are skills, tools, state, handoffs, guardrails, receipts, and provider adapters.

Opinions SOLID was waiting for agents.
Join newsletter

SOLID was born in object-oriented software, but its real subject was never classes. Its subject was change. Agents make that harder to hide because an agent can change its role, tools, memory, authority, model, data source, UI, and proof obligation in the same run.

The thesis:

SOLID for agents is not a nostalgia exercise. It is the fastest way to tell whether an agent system has real boundaries or only a convincing personality. The unit is no longer the class. The unit is the skill, the tool interface, the state contract, and the receipt another operator can inherit.

Robert C. Martin's 2020 restatement of SOLID says SRP means grouping things that change for the same reasons and separating things that change for different reasons. That is exactly the agent problem. A bad class becomes a god object. A bad agent becomes a god intern: it reads everything, calls everything, remembers vaguely, explains confidently, and leaves no replayable evidence.

The counterintuitive research finding

The agent ecosystem is not converging on raw autonomy. It is converging on bureaucracy. Skills package procedures. MCP formalizes tools, consent, schemas, roots, and sampling. Apps split transcript-visible data from component-only hydration state. Enterprise frameworks add checkpointing, human review, type-safe routing, memory, middleware, and telemetry. Connector platforms turn apps into governed action surfaces. Sandbox clouds separate execution from reasoning. Web-data tools make acquisition a bounded capability.

That is not the language of magic. It is the language of institutions.

01

The best context is often unloaded.

Agent Skills work because the agent sees tiny metadata first, then loads SKILL.md, references, and scripts only when needed. Unused expertise sitting on disk can be more useful than a giant prompt already crowding the model.

02

Permission is not a description.

MCP says tool descriptions and annotations should be treated cautiously. Apps SDK read-only, destructive, and open-world hints help the host frame approval, but servers still have to enforce authorization.

03

A handoff is a tool call wearing a social mask.

OpenAI's Agents SDK represents handoffs to other agents as tools. That makes Liskov concrete: a substitute agent is safe only if the interface, authority, output, and verifier still mean the same thing.

04

Memory is a dependency, not a soul.

Stateful runtimes and sessions are powerful, but every memory surface becomes a reason to change. The right question is not "does it remember?" It is "which state is allowed to affect this run?"

05

Receipts compete with privacy.

Traces, skills, and run logs make systems inspectable, but the same proof surface can create retention and exposure risk. Auditability is not free; the receipt needs its own scope.

06

The UI boundary is an architecture boundary.

Apps SDK structuredContent and content are visible to the transcript, while _meta hydrates the component without being shown to the model. That is dependency inversion in product form.

The new SOLID

Letter Agent meaning Design test
S Scoped responsibility Does this skill have one operational reason to change?
O Open skill extension Can capability be added as a package, not prompt surgery?
L Liskov contracts Can another agent, model, or human satisfy the same contract safely?
I Interface-minimal tools Does the agent see only the tools it needs for this authority level?
D Dependency-inverted runtime Does mission logic depend on capability contracts instead of vendors?

S: one operational reason to change

Classic SRP asks whether a module has one reason to change. For agents, the sharper version is this:

An agent or skill should have one operational reason to change.

Role names are not enough. A "research agent" that can scrape the web, edit the repo, deploy the site, send email, update CRM, and rewrite memory is not single-purpose just because it has a tidy name. Its responsibilities change when sources change, when tool permissions change, when proof requirements change, when state changes, when the artifact changes, and when the rollback path changes.

A better split follows the receipt:

  • Research skill: gather sources and uncertainty.
  • Synthesis skill: turn evidence into claims and counterclaims.
  • Build skill: change files inside a known workspace.
  • Review skill: verify contracts, tests, links, and claims.
  • Publish skill: push, deploy, and prove the public route.
  • Receipt skill: record what changed, what passed, what failed, and who inherits it.

The design test is simple: if two users would ask for different verifiers, you probably have two responsibilities.

O: extend with skills, not prompt surgery

Open/Closed becomes obvious once skills enter the picture. Anthropic's Skills docs and the Agent Skills specification both describe a filesystem package: metadata, a SKILL.md, and optional scripts, references, and assets. The important part is progressive disclosure. Metadata is cheap. Instructions load when the task triggers them. Resources and code load only when needed.

That is OCP for agents. The core agent remains closed to random mega-prompt edits and open to new capability packages.

A prompt library is advice. A skill package is installable operational memory. It can carry a validator script, a template, a reference, a fixture, and a receipt format. It can also be audited as software, which matters because Anthropic explicitly warns that skills can contain instructions and code capable of tool misuse, data exposure, and external dependency risk.

Rule: when a workflow repeats three times, stop improving the prompt. Create a skill.

L: substitute by contract, not vibes

Liskov is often taught as inheritance trivia. Martin's restatement is more useful: a user of an interface must not be confused by an implementation of that interface. That is the entire agent-substitution problem.

You should be able to swap a hosted model for a local model, a human reviewer for an AI reviewer, a connector layer for a direct API, or a sandbox for a local shell. But only if the contract holds.

agent_contract:
  input: task, allowed_sources, tool_scope
  authority: read, write, delete, deploy, send
  output: artifact, claim, evidence, uncertainty
  verifier: command, human review, source check, browser proof
  receipt: state_before, action_taken, state_after, risk
  rollback: path_if_wrong

OpenAI's Agents SDK makes this concrete: agents, tools, handoffs, guardrails, sessions, tracing, MCP integration, and sandbox agents are separate primitives. Handoffs are represented to the model as tools. Guardrails run at specific points in the workflow, and tool guardrails do not automatically apply to every hosted or handoff path. That means the interface has to say where validation actually happens.

Rule: never substitute an agent by personality. Substitute by artifact contract.

I: split tools by authority

Interface Segregation becomes security design for agents. The failure mode is the tool buffet: one agent sees shell, browser, database, deploy, email, calendar, payments, files, app connectors, and cloud consoles. Then the model has to infer not only what to do, but what not to touch.

The right split is not by vendor. It is by authority:

  • Read tools: search, list, fetch, inspect, summarize.
  • Draft tools: propose, render, simulate, stage.
  • Write tools: edit, create, update, label.
  • Destructive tools: delete, overwrite, revoke, reset.
  • External-action tools: send, buy, post, deploy, publish.
  • Host-operation tools: shell, filesystem, sandbox, credentials.

MCP's trust language is blunt: tools are arbitrary code execution and users should understand and authorize tool use. Its roots feature is even more revealing: roots tell servers what directories are relevant, but the spec says they are informational guidance, not access control. The product lesson is uncomfortable: tool surfaces can describe boundaries, but enforcement still lives in the host, server, and verifier.

Rule: if approval would differ, the interface should differ.

D: depend on capabilities, not vendors

Dependency Inversion for agents says the mission should depend on capability contracts, not a specific model, SDK, connector, or UI host.

Bad:

Use one named model, this exact browser plugin, this exact CRM action,
and this exact host UI to update the workflow.

Better:

capabilities:
  retrieve_account_context
  draft_update
  validate_policy
  request_approval
  write_final_change
  emit_receipt

Then inject the adapters: OpenAI, Anthropic, local vLLM, Microsoft Agent Framework, Pydantic AI, Vercel AI SDK, Google ADK, CrewAI, Zapier MCP, Composio, E2B, Firecrawl, or a human reviewer. The provider matters, but it should not own the mission logic.

The most interesting DIP example is not a model adapter. It is Apps SDK _meta. The component can receive hydration data hidden from the model while transcript-visible structuredContent remains concise. The high-level mission does not depend on every UI detail. It depends on the declared result contract.

The anti-patterns

  1. God agent: one agent owns research, writing, build, deploy, email, and memory.
  2. Mega prompt: every new capability becomes another instruction paragraph.
  3. Tool buffet: every integration is visible to every run.
  4. Memory theater: the system "remembers" but cannot replay evidence.
  5. Invisible authority: no one can say what the agent was allowed to do.
  6. Unreceipted output: the answer exists, but the proof trail does not.
  7. Provider lock-in by accident: business logic quietly depends on one runtime quirk.

The cure is not more prompting. The cure is smaller agents, richer skills, narrower tools, explicit state, stronger verifiers, and receipts that survive the session.

The Chopshopr pattern

Chopshopr is strongest when it treats AI work as an inspectable operating surface: prompt, model or tool boundary, artifact, source trail, verifier, receipt, and next owner. The trust-packet article already names the safe unit: intent, authority, context, constraints, tool scope, evidence, verifier, receipt, rollback, and handoff. SOLID gives that packet a design vocabulary.

A serious agent run should end like this:

solid_trust_packet:
  claim: what changed or should change
  responsibility: one reason this skill exists
  extension_point: how to add capability without prompt edits
  substitution_contract: what another agent must preserve
  exposed_interfaces: tools visible to this run
  dependencies: capability contracts and concrete adapters
  evidence: files, links, traces, screenshots, commands
  verifier: pass/fail check
  rollback: what to do if wrong
  next_owner: human, agent, queue, or route

Build the Agent SOLID Audit next

The smallest useful product surface is not another manifesto. It is a lab that turns a messy agent description into a scorecard and one generated trust packet.

Input Audit output Receipt field
Agent description SRP score and recommended split responsibility
Installed skills OCP score and missing package boundary extension_point
Inputs and outputs LSP score and broken substitution risks substitution_contract
Tools exposed ISP score and authority overreach exposed_interfaces
Models, connectors, runtimes DIP score and vendor-coupling risks dependencies

One bad example should be enough for the first release: a god agent with shell, email, browser, CRM, deploy, and memory access. The lab should return the top three violations, the skill split, the exact tools each skill may see, and a trust packet a stranger can inspect in under a minute.

The one-minute trust test

Ask this before calling the system production-ready:

Could a stranger trust the smallest agent run in under 60 seconds because the receipt names the goal, allowed tools, state before, action taken, state after, verifier, rollback, and next owner?

If yes, the agent has architecture. If no, the agent has theater.

Sources