!Five agent runtimes share skills through a luminous physical network.
*Published: August 26, 2026*
*This is Part 6. Part 1 covers the foundation, Part 2 email and tasks, Part 3 circuit breakers and dream mode, Part 4 the Obsidian brain, and Part 5 the ecosystem.*
---
A reader sent me a question this week. He is a student, he builds things, and he asked something I had genuinely never sat down and answered properly:
> "I remember you writing about multiple models workflow, like using codex to review Claude work. Do you transfer plugins, hooks, skills, MCPs between different providers, like Claude Code and Codex? If yes, then how? What about local models?"
I typed half an answer, deleted it, and realised I did not actually know. I had five coding agents running on one laptop and a comfortable story about how they shared things. So I went and measured it instead.
**Short version: skills transfer. Almost nothing else does. And while I was checking, I found six things quietly broken in my own setup, including one that had been dead for eight days.**
Here is the real answer.
!A shared skills tree connects five agent runtimes.
---
Five agent runtimes live on this MacBook. They are not five models. They are five different programs, each with its own config format, its own extension system, and its own idea of what an "extension" even is.
- **Claude Code**, with my full personality framework loaded. This is the one that does the orchestration. - A second Claude Code, deliberately clean. Same binary, different config directory, no personality, minimal permissions. It exists so I can get a second opinion that has not read my own doctrine and cannot inherit my assumptions. - **Codex**, OpenAI's CLI, which I use as an independent reviewer. - A third-party agent CLI I keep as an extra lane. - **A multi-agent runtime** that hosts six named agents with their own memories, models and boards. That one is Part 8's story.
The two Claude Code instances are the interesting trick and it costs nothing: one environment variable selects the config directory. That is the entire isolation mechanism. One is 58 hook commands and 15 plugins. The other is one hook and one plugin. Same binary, two different animals.
---
A "skill" here is a folder with a markdown file in it. That is the whole format. Instructions, examples, maybe a script.
Because a skill is just a directory, it transfers the way any directory transfers: **a symlink.**
I keep the real skills in one place and symlink them into each runtime. Edit one file, and every agent that reads it sees the change immediately. No sync job, no build step, no conversion, nothing to keep in step.
Measured on my machine as I wrote this: 99 real skill directories, and 125 symlinks across five trees.
One runtime does not even use symlinks. Its config just names the shared directory as an extra path to read. Same result, less machinery.
I wanted to call it "one hub, four spokes." Then I drew the graph. It is not that.
**The real topology is messier.** One runtime links into the shared tree, but also into a project tree, a developer tree, and a nested tree that should not exist. Another links partly into the shared tree and partly into the first runtime. It grew that way. Nobody designed it.
So the honest description is a primary shared source inside a mixed graph, not a hub. I would draw the graph occasionally. Mine had drifted. I had not noticed.
**Seventeen symlinks pointed at nothing.** Sixteen aimed at a project directory I deleted months ago; the seventeenth at an app that no longer ships the file it used to. That is the cost of sharing by symlink: delete one directory and links break in two runtimes at once, silently, because a broken symlink does not announce itself.
Two skills were copies pretending to be shared. Same name, same content, different files. I could have edited one for weeks and wondered why the other runtime never picked it up. I checked with one command and got two different inode numbers, which is the moment the illusion falls apart.
Both are fixed now. Both had been sitting there for months while I told people my skills were shared.
<details> <summary>The commands, if you want to check your own</summary>
```bash
find <skills-dir> -maxdepth 1 -type l ! -exec test -e {} \; -print
stat -f '%i %N' <path-a>/SKILL.md <path-b>/SKILL.md
``` </details>
---
!Separate configuration systems stop short of joining.
MCP is the protocol that lets an agent talk to external tools. Every runtime supports it. **None of them share the configuration.**
- One declares its servers in JSON. - One declares them in TOML. - One declares them in YAML, and then again separately per agent profile.
Three syntaxes, four separate files. One web-fetching server I use everywhere is declared four times, by hand, in four places. If I change its settings I change them four times, or I get four different behaviours and no warning.
There is no shared format, no import between them, no single source. The same tool in every agent means writing it out every time. **This is the part of the ecosystem that has not been solved by anyone yet.**
---
Hooks are where an agent lets you run your own code at a specific moment: before a tool call, when a session starts, when it tries to stop.
This is where the runtimes diverge hardest.
Claude Code has twelve distinct hook events. Before a tool, after a tool, session start, session end, before compaction, after compaction, when the model tries to stop, when a subagent stops, and more. I have 58 hook commands registered across them.
**The multi-agent runtime has exactly one.** Before a tool call. That is the whole surface.
You cannot translate twelve events into one. There is no sensible mapping, and pretending there is would produce something that looks configured and does nothing. So hooks do not transfer between those two, full stop.
But there is a bridge, and I did not build it. Claude Code and Codex happen to accept the *same hook JSON dialect*. I noticed because a plugin I installed ships a single hook file that both runtimes read, along with sibling files for two other agent CLIs. The plugin author had already worked out that these formats had converged and shipped one file for several hosts.
That is a de facto standard emerging from the plugin ecosystem rather than from any vendor. It is the most interesting thing I found all week.
⚠️ **It is a shared dialect, not shared behaviour.** The two copies on my machine had already drifted a minor version apart, and one had an extra event the other did not.
---
!Matching plugin cartridges drift apart on separate docks.
Claude Code and Codex use the same plugin package format. The same author can publish once and both can install it.
But each keeps its own cache and its own copy. Nothing is linked. Mine were already at different versions of the same plugin and had been for a while.
So plugins do not transfer. They get installed twice and then drift.
The other runtime's plugin system is unrelated entirely: Python modules, different registry, different concept.
---
!Several workstations connect to one protected memory core.
I run a small memory service that several runtimes read and write to. Three separate clients, one shared store, over a private network between my machines.
**I nearly published that this was offline.** One of my research passes reported the network was down and the service unreachable, and I believed it, because it fit a pattern I had already seen three times that day.
Then I had two other models check my findings. Both refuted it in the same way: the network is up, the service answers, and there was a write from a third client dated that same day. The claim was not partly wrong. It was wrong.
I am including this because it is the most useful thing in this article. **I had a story, the story fit the evidence I had gathered, and it was false.** Why did it not go into the post? I asked two other systems to prove me wrong first.
---
!A local model gate keeps cloud traffic out.
My original answer was "local models reach exactly one of my runtimes."
That is wrong, and the reviewer that caught it did not just say so, it ran the command. Codex has native support for local models. Point it at a local server and it talks to it. It replied with the exact test string I asked for.
So the accurate answer is: **one runtime has local models configured as a default provider, and at least one other can use them on demand.** Configured and capable are different questions, and I had conflated them.
⚠️ One trap worth knowing. My local model runner lists four models. Two of them are **cloud passthroughs** that happen to be listed identically to the local ones. Nothing in the listing tells you which is which except a suffix on the name.
That matters for anything private. My voice transcription pipeline handles personal recordings. It refuses any model whose name ends in the cloud suffix. Not because I am paranoid, but because the list gives you no other signal, and "runs locally" is the entire promise.
---
There is exactly one tool in my setup that genuinely converts between agent configurations. It reads Claude Code's or Codex's directory and produces the multi-agent runtime's format: instructions become memory entries, allowed-command rules become its own allow list, MCP declarations get mapped across.
It also strips anything that looks like a credential on the way through.
It is one-way and one-shot. No reverse path, no watch mode, no reconciliation if you run it twice. It converts config *in*, and that is all.
⚠️ **And one caution I only found by testing it**: the credential stripping is a heuristic on field names. A credential with an unusual name survives. The source comment promises more than the implementation delivers, which is worth knowing before you trust it with anything sensitive.
---
I went looking to answer one reader's question. I found six things wrong. Every single one of them was reporting success.
The worst was a sync between my task manager and my notes. It had been dead for **eight days**, and I had been reading the stale board that whole time believing it was current.
The cause is the best bug I have hit all year. The script runs a small Python program and reads its output. Inside that Python program is a comment explaining how the sync survives one of my editor plugins. That comment contains the phrase "a card's text."
The apostrophe in "card's" killed it.
The version of bash that ships with macOS is old enough that it scans that whole construct for a closing bracket *before* it understands where the embedded program starts and ends. So it treated that apostrophe as the beginning of a quoted string, never found the end, and gave up before running a single line.
**The comment explaining how the mechanism works is what destroyed the mechanism.**
Two things made it survive eight days rather than eight minutes:
It exited with a success code. I measured this afterwards. The failure mode returns zero. So anything checking "did the job succeed" saw a clean run on a script that had aborted before doing anything.
**My health check was not watching that job.** It watched four jobs, by name, typed by hand. The broken one was not on the list. That is not bad luck: a hand-maintained watchlist only grows when someone remembers to grow it, so the thing you forget to add is precisely the thing nobody is watching.
Both are fixed. The watchlist is now discovered automatically, so a new job is watched the moment it exists. And every scheduled job now writes a heartbeat as its final act, so the check asks **"did the work happen"** rather than "did the process end."
That second change is the one that generalises. That is the bit I keep coming back to.
<details> <summary>Why "did it exit cleanly" is the wrong question</summary>
An exit code tells you a process finished. It tells you nothing about whether it did its job. I have now hit this three times in one day, in three different systems:
- Two automation workflows reporting 100% success every five minutes, with zero-second durations and empty results. One had never executed successfully in its entire life. - My own review script reporting a clean, quiet day over a day with fifteen real events, because it compared a timestamp integer to a formatted date string and the filter silently matched nothing. - The health check above.
The fix in all three cases is the same shape: **make the job leave a mark when it does real work, and check the mark.**
</details>
---
Skills transfer, because they are folders and folders symlink. Write once, live everywhere.
**MCP servers do not.** Three syntaxes, declared by hand, per runtime.
Hooks do not, between systems with genuinely different event models. Between two that converged, one plugin file can serve both, and that convergence came from plugin authors rather than vendors.
**Plugins get reinstalled and then drift.**
Memory can be genuinely shared if you run a service for it, which is a real thing to operate.
**Local models are more available than I assumed**, and the label "local" deserves checking rather than trusting.
And one converter exists, one-way, useful once.
---
**Put your skills in one directory and link them everywhere.** It is the highest-leverage thing on this list and it takes five minutes.
I would not try to unify the rest. I spent a week believing my setup was more unified than it was. The formats have not converged and pretending they have produces something that looks configured and quietly does nothing.
**Check claims before publishing them.** I had two other models review my findings before this article. They refuted five things I was about to write, including one where I would have told you a working system was offline.
That is the same trick as using one model to review another's code, pointed at my own beliefs instead of at my code. It is uncomfortable and it works.
---
*Part 7 is the phone half: how a voice memo recorded on a walk becomes work an agent picks up, and why a shell script cannot legally read your own files on a Mac.*
*This is Part 6 of the EMMI-AI Infrastructure series.* *Part 1: I Built an AI That Learns From Its Own Mistakes* *Part 2: My AI Now Reads My Emails and Manages My Tasks* *Part 3: Building a Self-Evolving AI on My Mac* *Part 4: I Gave My AI Its Own Brain* *Part 5: The Ecosystem*
*Built by Emanuel Covasa in County Leitrim, with more measuring than I expected.*
#AI #ClaudeCode #Codex #DeveloperTools #PersonalAI #MCP #Neurodivergent #ADHD #BuildInPublic