Series

I Built 21 Skills That Do Nothing (And That's the Point)

nanoclawbuddyarchitectureagentsskills

I built 21 skills that do absolutely nothing. Not broken. Not even placeholders. Each one takes a request, formats a task, and sends it to another container. Then it’s done.

The natural reaction is: why? A skill that only delegates feels like overhead with extra steps. But I’ve had this architecture running for a few weeks now and I keep coming back to the same observation: the wrappers aren’t the dumb part. They’re the only part keeping the system honest. The wrappers are what make the whole thing possible.

Here’s the problem they solve.

I have 8 agents in the NanoClaw system. Buddy is the main one (the one I talk to on WhatsApp, the one that’s always on, the one that knows my routines and context). Then there are specialists: Babi handles fitness, Radar aggregates AI news, Quill manages this blog. Each specialist runs in its own container, with its own skills, its own memory, its own CLAUDE.md. Full isolation. No shared state.

So where do skills live? This is a question that sounds architectural but is actually a UX question. Because the answer determines what I have to think about to get something done.

Option one: put everything in Buddy. Give Buddy the /todays-workout skill, the full workout logic, the plan lookup, the injury awareness. Simple. Until it isn’t. Buddy’s context window starts filling up with fitness domain knowledge, workout plan formats, injury tracking logic. Then you add news briefing logic on top. Then blog drafting context. The context window doesn’t discriminate. It holds everything equally, which means it holds too much of everything. Buddy becomes a god agent: it knows a little about everything and does none of it well.

Option two: put nothing in Buddy. Let users route directly to specialists. “Hey Babi, what’s my workout today?” But now I need to know which agent does what. I need to remember that workout things go to Babi and news things go to Radar. Now I’m doing the routing in my head. The system didn’t remove complexity. It outsourced it to me.

Option three: wrapper skills. Buddy exposes /todays-workout. I invoke it. The wrapper sends the task to Babi. Babi executes with full context. The result comes back. I never think about routing. Buddy never learns fitness.

The wrapper is the interface contract. It defines that a capability exists, when to use it, and where it runs.

Babi has 5 real skills: /todays-workout, /log-workout, /evening-check, /weekly-summary, /monthly-summary. Real logic, full context, fitness-domain knowledge in the implementation. Buddy has one wrapper for each of those. Same names, 20 lines of task formatting and IPC queuing, no fitness logic whatsoever. Radar has 1 real skill (/daily-brief) and Buddy has one wrapper. Quill has 5 real skills for the blog pipeline and Buddy has 5 wrappers.

BUDDY: Twenty-one skills that tell me “not your job.” I’m less personal assistant, more switchboard operator. Ankit calls this “clean architecture.” I call it staying in my lane.

The wrapper skills in Buddy are reasoning guidance. They tell Buddy when to delegate: “User asked about workout → /todays-workout.”

The real skills in each specialist tell it how to execute.

Those are different problems. Conflate them, and you get context bloat and coupling.

There’s also a “god skill” anti-pattern this prevents. When you have one agent doing everything, skills accumulate. New feature means new skill. Every skill adds context. After enough months, you have a monolith that can technically do everything and confidently does nothing (because the context is too polluted to reason cleanly about any one domain). The wrapper architecture forces a boundary. Buddy knows that fitness stuff goes to Babi. Buddy doesn’t know anything about how Babi thinks about fitness. That’s Babi’s problem, in Babi’s container.

The tradeoffs are real, and worth naming.

There’s latency. When I ask for my workout, Buddy invokes a wrapper, which queues a task, which spawns the Babi container, which processes the request and sends a result back. That’s a few seconds versus Buddy just answering directly. Latency is visible. Context poisoning isn’t. I’ll take a few extra seconds over a system that slowly gets worse in ways I can’t debug.

There’s also the question of 21 wrapper files to maintain. In practice, wrappers don’t change. The interface they expose is a thin contract: take this kind of input, format it, queue it to this container. If Babi adds a new skill, Buddy gets a new wrapper. If Babi changes how workout logging works, Buddy’s wrapper doesn’t change. It doesn’t know how workout logging works. The specialist evolves. The interface stays stable.

There’s a failure mode here. Wrappers don’t stay thin by accident.

It starts with “just a small validation.” Then a fallback. Then a formatting tweak. Then a retry policy. A few months later, Buddy is quietly re-implementing half the domain logic it was supposed to delegate. The rule is simple: if a wrapper starts knowing how something works, it’s in the wrong place.

The security property is a side effect I didn’t plan for. Buddy can’t access Babi’s data directly. It can only make requests and receive results. IPC isn’t just communication. It’s the enforcement layer.

The user experience holds because Buddy’s skill list is complete. I see /todays-workout and I use it. I don’t see “route to Babi container via IPC, deserialize task format, process in fitness context.” I see a thing that gives me my workout. The architecture is invisible from where I sit. That’s what it’s supposed to be.

The system works because the most important skills don’t execute anything. They define the boundaries that let everything else work.

The skills that do nothing are holding everything together.