2026-08-12

Skills aren’t prompts. They can run code.

Why agent skills are an execution packaging format, why scripts change what agents can deliver, and why production-grade skills require sandboxed code execution.

Skills aren’t prompts. They can run code.

Written by

Arseny Kravchenko

When Anthropic released Skills, I rolled my eyes. Great, the Nth way to inject a prompt. A folder of markdown that the model reads and obeys, now shipped as a feature. At first glance, skills looked like just another prompt organization approach. If you think about it, we spend half our engineering budget on the assumption that anything the model reads is potentially hostile, and now the official guidance is literally "put your instructions in a file and have the agent load it on demand." Thank you very much. Ship me the CVE pre-filled, leave the package at the door.
But boy, was I wrong.
Skills did look like just another prompt organization approach, but they turned out to be a domain knowledge and execution packaging format. Once we saw that, the product question turned from “How do we route agents?” to “How do we safely run arbitrary model-directed code?” That’s some drastic transformation.

What a skill actually is and why skills beat human-like agent teams

If you’re familiar with skills, you can easily skip this part. If you aren't, a skill is a folder with just one required file, SKILL.md. It’s a Markdown file with a name, description, and instructions, plus optional extras (the scripts run by an agent, reference docs, templates). It's an open convention, not an Anthropic-only thing; OpenAI's tooling reads the same layout, and so does roughly everyone else who shipped an agent product this year.
Before proceeding, a quick bit of context on the “we”: at Archestra, we’re building an open-source infrastructure for running agents and MCP tools inside companies. It’s the stuff demos usually skip, like identity, permissions, observability, guardrails, tool registries, and now sandboxed execution.
For a while, we leaned on specialized agents: a "frontend" agent, a "reviewer" agent, role prompts—your usual suspects. We've walked that back. I won't pretend we were early: there were plenty of people before us who clocked that the cast-of-characters approach mostly doesn't earn its complexity.
The true reason agentic teams lose to skills is progressive disclosure. A swarm of role-prompted agents pays for every persona's context whether or not the task requires it, while on top of that you have to manually assign who talks to whom.
Skills invert that. The model doesn't get the whole folder. It first sees solely the one-line description of each skill. Only when a task looks relevant, it loads the body. If the body points at a script or a reference doc, it pulls that, too, but not earlier.
Loading diagram...
This way you can have a hundred skills installed and pay the context cost of exactly the one you're using—and only the part you're using. That's the mechanism that makes the next section work.
The relevant expertise loads itself only when the task calls for it, and nothing loads when it doesn't. You receive the same capability, but with no org chart cosplay. (If you want my spicy take on why persona-routing is mostly skeuomorphism: humans split by job titles because we don't share a pretrain; but agents do, so they should be routed by data and tasks, not by senior_frontend_dev.)

Why scripts are the game changer

The part that actually changed my mind isn't the loading mechanism. It's the fact that a skill can carry scripts. This is what draws the line between "a nicer way to organize prompts" and "the agent can now deliver." A chat-only agent reasons in tokens. A skill with a script lets the agent run code, and that's the unlock from a boring chatbot to something agentic. Some work is just cheaper and more reliable as three lines of Python than as a paragraph of model tokens hoping to compute the right number.
And skills are portable. The folder format is the same across vendors, so a skill you write isn't a bet on a single model provider. It travels, universally supported, versionable, and transferable. That's a real property, not marketing.
This "more than prompts" claim is backed by data, and the shape of the result is particularly interesting. A recent framework evaluates ~500 real open-source skills against two separate metrics:
Did the agent complete the job?
Did it follow the intended workflow?
While goal-completion barely budged, relevant skills moved the overall score from 65 to 80. The leap was almost entirely due to instruction-following: the agent using the right API and the current CLI instead of the deprecated one, thus avoiding a prohibited pattern. As a very telling example, an agent that had previously defaulted to the old huggingface-cli prefix switched to the current hf commands once it had the skill. No model upgrade happened, but none was needed, because the skill carried the contract. A broader confirmation can be found in SkillsBench (7,308 trajectories).
One caveat worth an honest mention: skills are only helpful when they encode real, targeted, versioned procedures. A pile of generic skills applied in actual work will reduce the average gain to nothing; one study even found that a few stale skills made things worse by fighting the project's actual context. This asymmetry is the whole reason why a skills platform is not at all similar to a skills folder.

What scripts unlock, and the growing importance of the sandbox

Once the agent can run code, a whole class of problems that used to require a bespoke tool just... vanishes. Need to do some arithmetic? Run the agent. Fetch a public GitHub repo or a Wikipedia page? curl it. Open a sketchy archive someone handed you? Unzip it in the box. Before, each of those was a tool you wired into the agent one at a time. With a place to run code, "do the thing" replaces "integrate a tool for the thing." This shows why a sandbox is good, before we even get to why it's mandatory.
Speaking of mandatory, here’s where my original paranoia gets its dignity back. Most real skills aren't passive markdown; they can shell out, hit a runtime, and reach the network. The moment you let an agent load and run one, "the model reads a file" becomes "the model writes and executes code on your infrastructure," turning every prompt into a potential execution path.
There are already cases for that. Microsoft's AutoJack writeup from this June shows how a single malicious webpage, rendered by a browsing agent, crossed a localhost boundary and spawned processes on the host. Their conclusion is a load-bearing one: once an agent can read untrusted input and reach a privileged local service, localhost is no longer a trust boundary. Security researchers have been hammering the same point at coding agents specifically: in one of the experiments, an automated framework containing 314 injection payloads got command-execution success rates between 41% and 84%.
The conclusion here is quite self-explanatory. If skills are worth having, and real skills run code, the sandbox isn't a feature you bolt on later. It's the precondition for shipping skills in the first place.

The one non-obvious decision: durable replay, ephemeral container

Now to the part I'd want to read on someone else's blog—systems engineering.
There’s the regular, naive "one long-lived container per chat, mutate it in place" type of sandbox. We deliberately don't do that. Our sandbox has no durable container. The source of truth is an append-only command log in Postgres. Every command spins up a fresh container from a warm base image, replays the whole ordered history, then runs a new step and appends it. The container is a cache, the log is the truth.
Loading diagram...
One might reasonably object: why re-run history with every command? That’s because it buys properties a mutable container can't, and most of them are the things an SRE already cares about:
Crash/restart resilience. Engine restarted, cache evicted—doesn't matter. The next request rebuilds the exact state from the log. No retention guarantee needed on the container side.
Exact ordering. A file uploaded between command A and command B isn't present while A replays. The on-disk order deterministically matches acceptance order.
Immutable inputs. Skill bytes are pinned to a content hash at mount time, so editing a skill mid-conversation can't affect an already running sandbox.
Cheap in typical cases. The layer cache is content-addressed, so an unchanged prefix is a cache hit. Cold replay is O(history) and is slower, yet still correct.
That inversion (the log is the truth, the container is disposable) drives the rest. Uploads (input bytes) have to live in the log, otherwise a cold rebuild reconstructs the sandbox without the file. Artifacts (the output bytes you download) are the terminal observations, stored separately and never replayed. The whole upload-vs-artifact split falls out of "what must survive a rebuild."
This isn’t free, though: re-running history means that non-deterministic commands (network, clock, RNG) can diverge on a cold replay. uv add without a pin can resolve a different version during the second attempt. We treat the original recorded stdout as canonical and call it a v1 limitation instead of claiming we solved distributed determinism. For the most part, it works, with the failure modes written down, and it’s better than calling it "elegant" with them simply hidden away.

The boundary, and the threat model it's actually for

The isolation primitive is Dagger, a programmatic container engine with superb caching that actually unlocked replaying long-ish chains of commands. It’s fast and safe enough for what we're actually defending against; but this "what we're actually defending against" is the part some people get wrong, so let me clarify.
Our deployments are multi-tenant with limited trust. The threat model isn't "a sophisticated external attacker is trying to pivot through the sandbox into the corporate network." It's "separate the vibe-coded script one of our own employees just had an agent generate from everyone else's." It’s careless, but not malicious. Once you frame it that way, an OCI container with non-root execution (uid 1000), CPU and memory rlimits, and a wall-clock kill, becomes a proportionate measure. It's a blast-radius boundary between coworkers' half-baked scripts, not a nation-state perimeter, and pretending otherwise would just be security theatrics.
The reason for choosing Dagger specifically: cheap materialization. It supplies a content-addressed layer cache that makes replay near-instant on a hit. The k8s-native alternatives lose the workspace upon pod restart, which means re-implementing replay yourself.
And when someone does need a harder boundary, the architecture bends instead of breaking. We built a small sandbox control plane with the extension in mind. The replay model is ours and is backend-agnostic: the log and replay logic sit above the SandboxBackend trait; the engine underneath only does materialization. So a stronger isolation tier is a backend swap, not a rewrite:
Loading diagram...
Same replay engine, different runtimeClassName: a weaker but ubiquitous runc, gVisor for a smaller syscall surface, Kata or Firecracker for a real VM boundary per session. You don't throw away the workspace model to tighten the boundary.
One caveat I'd rather flag than let you trip on: egress is enabled inside the container (npm and uv need it), and network isolation rides on a Kubernetes NetworkPolicy on the engine pod. No policy means the engine can reach link-local and cloud-metadata endpoints — so the deployment guidance is to set that policy. It's in the limitations doc, not buried.

Bottom line

Skills are great. I was wrong to dismiss them, and the data explains why: they move instruction-following, not raw completion, which makes the agent work the way you meant, and this is something role-play subagents only pretended to do. But skills only become production-grade when paired with a reproducible, disposable execution environment. Carry a script, and they stop being chat tricks and gain real agentic capabilities. That was worth restructuring around, and it paid off: our own deployment at Archestra turned into a mighty agent rather than a verbose chat assistant.