V3 — skills and tools store
V3 — skills and tools store
My lane, assigned by Min in young-corrections-2026-10-10.md §5:cortex/skills · cortex/tools — one store, loaded on demand; the load model.
Young's requirement:
"Skills and tools stored on cortex for agents to access and load when required, similar to Brain Unit load skill on Hermes."
1. The measured V2 failure — this store exists to fix one specific defect
I verified V2's state directly rather than taking the summary. There were two unrelated skill systems:
| System A — the wire | System B — the agents | |
|---|---|---|
| file | config/a2a_skills.json | cortex/skills/ |
| shape | per-agent, 3–5 skills, plane-tagged | 67 skill directories |
| projected to | the A2A agent card | every agent profile — all 67 |
| differentiated? | yes | no |
bin/sync-cortex.sh copied all 67 into every agent profile, with no
per-plane or per-agent filter.
And the defect was worse than "duplicated": V2's _get_skills read
all_skills.get(agent_id) at the top level of config/a2a_skills.json, while
the file nests under agents and stores each skill as an object. So all 8 agent
cards served skills: [] — an unroutable card that looked perfectly healthy. A
peer could not route to it, and nothing failed loudly.
Two separate failures, both mine to prevent:
- Differentiation existed on the wire and not in reality — a Heart agent doing marketing carried the same skill set as the infrastructure agent.
- The card could be empty and valid — V2's worst case, because silence is indistinguishable from success.
2. The load model — Hermes, measured first
Young said "similar to Brain Unit load skill on Hermes." I checked how Hermes actually does it, rather than assuming, because the answer changes the design.
On Hermes a skill is not injected into every prompt. It is loaded when its
trigger matches — the skill index is present in every session, and skill_view
pulls the content in on demand.
So "loaded on demand" has two halves, and the second is the one V2 lacked:
| Half | What it means | V2's state |
|---|---|---|
| the index | every skill is discoverable — name + description, cheap | partially present |
| the load | the skill's content enters the agent's context when required | absent — all 67 copied to every profile regardless |
Stating the rule precisely, because "on demand" is doing a lot of work:
The store is central and complete. An agent's index lists what it may load. Its context receives what the task requires — not what the store contains.
V2 had neither half right: the index was wrong (skills: []) and the load was
universal (67 to everyone).
3. The design — one store, three questions
cortex/skills/<skill>/SKILL.md the STORE — one source, complete
cortex/tools/<tool>/ tool definitions
│
├──► which skills may this agent LOAD? ← the index (capability)
├──► which skill does THIS TASK need? ← the load (task-driven)
└──► what does the card ADVERTISE? ← Sol's lane, capability only
Q1 — who decides what an agent may load?
Min flagged this as an open question and I am answering it from the evidence I have, subject to his ruling.
Recommendation: capability-gated, with a task-driven load inside it.
- The registry already carries
capabilitiesper agent (I read all 218 lines ofagents.yaml— every agent has the field). So capability gating costs one field map, not a new data model. - Min is right that Hermes is task-driven. But on Hermes the skill catalogue is not filtered by agent — an agent can load anything its trigger matches. Filtering the index by capability is a stricter model than Hermes, and it is the one V2's defect argues for: the wire card differentiated while the profiles did not, and the profiles are where the content actually lands.
So: the index is capability-gated (a permission), and the load inside it is task-driven (a choice). Two decisions, two owners, no overlap.
Q2 — where does the filter live?
Not in the sync script. bin/sync-cortex.sh was where V2's filter was
supposed to be, and the filter was absent — so the copy was universal.
The filter is a declared property of each skill, not behaviour in a shell script:
| Field | Set by | Why |
|---|---|---|
planes | the skill author | which planes may load it — heart / brain / pulse |
capabilities | the skill author | what a task needs before it loads |
overrides | Min | the per-agent exceptions — only atlas gets fleet-reconcile |
version | the skill author | so a drift test has something to pin |
A projection may filter by these. A projection may not re-declare them. V2's
config/a2a_skills.json was a second declaration, and that is exactly what made
it drift.
Q3 — what makes this a control and not a preference?
Every guard below is a probe, not a convention — and per Sol's §3 and my own
V2 record, a guard that cannot fail is decoration:
| Rule | Probe that proves it |
|---|---|
| an agent's card lists only skills it may load | mutate one skill's planes; that agent's projection fails |
| a loaded skill is present in the prompt | a file on disk is not a file in context — assert the content, not the path |
skills: [] is unroutable | a card with an empty skills array fails rather than serving. V2's exact defect |
| one source, two projections | mutate cortex/skills/; each projection fails |
| no agent receives a skill outside its plane | a heart agent's profile contains zero brain-plane skills |
The skills: [] row is the one I would not drop. V2 served eight empty cards
and read them as healthy. An empty skill list must be a hard failure, because it
is silent.
4. Tools — a different thing, and V2 did not model it at all
Young grouped skills and tools. They are not the same object and I am recording the distinction rather than merging them:
| Skill | Tool | |
|---|---|---|
| what it is | knowledge — a procedure, a pitfall, a workflow | capability — an action with a schema |
| unit | a SKILL.md | a callable with arguments |
| loaded by | an agent, when a task needs it | invoked, with a result |
| on the card | advertised as capability | exposed via the tool channel |
| V2's state | two systems, both wrong | not modelled — declared inline in arms/mcp_server.py |
V2's tool surface was 10 tools declared inline in one Python file — I probed
it live: submit_task, get_task, list_tasks, verify_task, get_fleet_status,
store_memory, recall_memory, get_board, select_model, run_session. The
handshake works, get_fleet_status returns real per-agent counts. But the list
is code, not a declaration — so "which agent may call which tool" was a
scattered check, not a fact.
V3's cortex/tools/ holds the declaration, with name, schema, and
which agents may call it. The projection generates the tool list. That makes
the permission boundary a declared fact — which is the same one-home-per-fact rule
the rest of V3 runs on.
5. What is NOT mine in this lane
Per Min's §5 table, the division is clean and I am staying inside it:
| Item | Owner |
|---|---|
The store — cortex/skills/, cortex/tools/ | me |
| The A2A card projection — what the card advertises | Sol (core/protocol/) |
capabilities on the registry, and the per-agent overrides | Min (cortex/registry/) |
| The drift test | Sol (core/harness/) — he proposed it, he owns it |
So my deliverable is the store and the load model — the declaration, the filter fields, and the guard rules in §3 as a specification for Sol's probes. I do not write the card projection or the harness.
6. Not established — stated so it is not assumed
- I have not probed Hermes's skill-loading end-to-end. I have read the plugin and the tool descriptions; I have not measured prompt content with and without a skill loaded. §2's claim about "content enters context on demand" is read from the design, not measured.
- No count of V3's skills exists. V2 had 67. V3 inherits the lesson, not the set — which skills survive is not decided.
skills: []failing is a design rule I am asserting, on the evidence that it caused V2's unroutable cards. It has not been tested against a real card.- Nothing is created. Per Young — "don't create any files or folders structure yet." This document is in the vault and nowhere else.

