The Floor Is a Design Knob

Yesterday's morph-as-tool refactor leaves a gap. Once the agent can author tools, the next question is what those tools are written in — and whether compilers and VMs are themselves tools. They are. Which means the privileged layer is a design knob; here's how low you can turn it.

Yesterday's post (053) made a structural argument: collapse the split between agent-decidable tools and developer-decidable transitions, and the agent can design new modes mid-conversation. Someone read it and asked one question I want to answer here, and a follow-up question I want to take seriously.

The first question: would the toy's DSL — its seven ops the runtime walks at execution time — make sense as a language that runs on a virtual machine with its own instruction set?

The second question, which deserves its own paragraph, was three words: *compilers and VMs are tools.*

---

The DSL is already an AST being walked

The toy lets the agent emit a structured op-spec and the runtime interprets it. That interpreter has a name in compiler-textbook land: it's a tree-walking evaluator. The op-spec is the AST. Make this explicit as a VM with a real instruction set and you've moved from a refinement, not a redesign.

The honest framing on why this matters: the seven ops work because every example I shipped is a literal. The estimator returns the result of lookup_options because the agent put lookup_options in the bytecode. The moment you want an invented tool that does any actual work — *quote = base_rate × sqft + materials, with a 20% margin if the scope says premium* — you need expressions, conditionals, and intermediate values. At that point you have three options, and only one is good:

  1. Add ad-hoc ops every time the agent needs something new. op: 'multiply', op: 'if_contains', op: 'sum'. The DSL becomes a junk drawer. The auditability claim from yesterday's post — "an unrecognised op gets logged and skipped" — collapses, because the union now has fifty members nobody can hold in their head. You haven't built a language; you've built a hash table of ad-hoc primitives nobody designed together.
  2. Let the agent emit JavaScript and `eval` it. Yesterday's post correctly rejected this. It's an RCE primitive with a sticker on it.
  3. Define a small VM. Stack- or register-based, with arithmetic, comparison, branching, field load/store, and a CALL instruction for invoking other registered tools. The agent emits bytecode; the runtime executes it.

Option 3 is what every system that's lived with this problem has converged on. Lua compiles to register-based bytecode for exactly this reason. The EVM exists because Ethereum needed agent-authored programs to be bounded and verifiable. WASM is the same idea generalised. You'd be in good company.

What the VM buys you

Five things the JSON-op interpreter can't easily give:

Static analysis as a safety surface. With a fixed instruction set and a typed memory model, you can prove things about an invented tool *before* executing it. "This bytecode never writes to auth_token." "This bytecode's worst-case execution is 2,400 instructions." That's the verification half Voyager has and yesterday's design didn't really — fixture-based tests catch one path each; static analysis covers all of them. The validation bullet I had to promote to a load-bearing section in 053 stops being bespoke and starts being mechanical.

Gas metering. A counter that decrements per instruction is one of the cleanest bounds on agent-authored code. Runaway tool? It hits the gas limit and halts. You can price tools by complexity. You can give the model a budget per turn and watch which inventions fit inside it. This is the discipline EVM forced on smart contracts; the same pressure is useful here.

Composition without model round-trips. A CALL instruction lets one invented tool invoke another internally — bounded, deterministic, no LLM in the loop. Right now every step in the toy costs a model turn. With CALL, the agent can author a tool that orchestrates three existing tools and the orchestration runs at bytecode speed, not at conversation speed.

Persistence becomes content addressing. A tool is {name, describe, bytecode} where bytecode is a byte array you can hash, sign, version, and diff. Yesterday's persistence question — *where do invented tools live* — gets a much cleaner answer. They live wherever you put a hash-addressed blob. Naming collisions become impossible (the hash *is* the name); rollback is a pointer change; signing tells you who authored what.

The senior/junior split gets clean. This is the part I'd put weight on. A heavy LLM authors bytecode — slow, expensive, big context. A lightweight VM executes it — cheap, fast, no model needed. That's exactly Voyager's architecture. Senior writes the skill, junior runs it forever. The VM is what makes the split structural rather than ad-hoc — it's what makes self-extension *economically* viable, not just architecturally elegant.

The cost: you're shipping a compiler and a VM, and "the model emits JSON ops a tree-walker can read" is a much friendlier authoring surface than "the model emits assembly." The mitigation is to keep the JSON-op shape as the *source* language the model writes in, and have a compile step produce bytecode for execution. The model never sees the VM. Safety and analysis live at the bytecode layer; the model gets the ergonomic authoring surface it already had.

That answers the first question.

Compilers and VMs are tools

Now the three-word follow-up. *Compilers and VMs are tools.*

Take it seriously and the architecture inverts in an interesting way. If compile_to_bytecode is a tool, and execute_bytecode is a tool, and define_tool is a tool, the agent isn't choosing between "JSON authoring" and "bytecode authoring." It's choosing a *pipeline* of tool calls that produce a callable thing. The seven-op DSL stops being The Authoring Language and becomes one frontend among several. The VM stops being infrastructure and becomes a callable capability. The "is this a DSL or a compiled language" question dissolves — it's both, neither, whichever the agent assembles for the case in front of it.

This is the recursion yesterday's post was gesturing at without quite naming. *Tools are control flow, all the way down.* Fine. But control flow over what? In the 053 draft, over a fixed runtime. Once compilation and execution are themselves tools, it's control flow over the runtime itself. The agent can author a tool whose body is "call parse_dsl, pipe the output into optimize, pipe that into compile, store the resulting bytecode in the registry under this name." It just built a compilation pipeline out of existing tools. Next turn, it uses that pipeline to author a *third* tool. The receptionist now has a tool that didn't exist, written in a language whose compiler also didn't exist five minutes ago, both products of the same uniform tool-call mechanism.

This is what makes the pattern actually distinctive versus Voyager. Voyager has a fixed authoring stack: model emits JS, validator runs fixtures, library stores. The stack is hard-wired; only the contents grow. In the morph-as-tool world, if you're consistent, *the stack itself is editable.* That's a much stronger claim than 053 made, and it's the one I'd build the post around if I were writing the canonical version.

But there has to be a floor

Here's where I'd push back on letting the recursion run free: something has to actually run when the music stops. If execute_bytecode is a tool, what executes it? You have two answers, and they're both honest:

  1. There's a privileged set of primitive tools the engine implements directly, and everything else is built on top. This is honest. It's also exactly the engine/agent boundary I claimed yesterday to dissolve, just moved one level down. I haven't removed the split, I've relocated it to a place where it bothers me less. That relocation is real value — the boundary is now at "instruction set," not at "mask transition" — but it's not a removal.
  2. The primitives are themselves expressed in some lower DSL, interpreted by an even more primitive engine. Turtles down until something is genuinely native code. This is the Lisp answer. It's intellectually beautiful. It's also true that at the bottom of every Lisp is a C interpreter someone wrote in a language that wasn't Lisp.

The right framing — and the framing I should have shipped yesterday — is to be explicit about the floor and argue that where you put it is *the actual design decision.* Three reference points:

| Floor location | What's fixed | What's editable |

|---|---|---|

| Voyager | JS runtime + validator + library | Skill contents |

| The 053 toy | The seven-op interpreter | Masks and tools above it |

| A VM-based version | A small instruction set (~20 ops) | Compilers, parsers, optimisers, the DSL itself |

Each step down buys flexibility and costs the ability to reason about what the agent is doing. Voyager can prove very little about agent skills statically, but everything above the JS line is in the agent's reach. A VM-floored version can prove a lot about bytecode but reasoning about a *compiler* the agent authored requires reasoning about how the compiler reasons, and that's harder.

The thing nobody can do — and this is worth saying plainly because the temptation is real — is have no floor. "Compilers and VMs are all tools" is true and useful as a design principle, but it doesn't follow that you can have a system with no privileged layer. The privileged layer is wherever you stop being able to introspect using the system's own tools. The interesting design question is how thin you can make it.

For yesterday's 600-line toy, I'd keep the floor where it is — seven ops, executed by a tree-walker, with compile and execute as future-work tools. For a real system I'd push down to a VM with maybe twenty instructions and treat everything above (parsers, optimisers, type-checkers, the DSL itself) as agent-editable tools. That's the version that earns the framing.

The unlock that makes it worth doing

If compilation is a tool, the agent can debug its own tools.

Tool failed at runtime? Call disassemble, inspect the bytecode, call recompile_with_logging, run again, observe the trace, patch the source, recompile. That's a feedback loop Voyager doesn't have because Voyager's compilation step is opaque to the agent — JS goes in, a callable comes out, and if it misbehaves the agent rewrites the source from scratch. With a transparent compile pipeline assembled from tools, the agent can trace, instrument, and refine. The same recursion that makes the architecture interesting makes the debugger natural.

Take this seriously and 053's argument changes shape. The post stops being about morph-as-tool specifically and starts being about *the privileged layer is a design knob, and here's how low you can turn it.* Morph-as-tool is one thing you get when you turn it down a notch. A self-modifying agent toolkit is what you get when you turn it down further. A self-modifying *compilation pipeline* is what you get when you turn it most of the way down — and even then, somewhere there's a floor someone wrote in a language that didn't grow itself.

That's the meatier thesis. The toy is already evidence for it. I just hadn't been brave enough yesterday to name what the toy was actually showing.

---

The Themis on the wall behind my screen had her eyes closed yesterday. The version on this post has them open. Not blindfolded, not weeping — looking past me, into the middle distance, watching whatever comes next. The cracked sword and the scales are still hers. What changed is what she's doing with them.

The same instruments. A different posture toward what they're for.

End of Transmission.