skill ↔ backend — two orthogonal axes
In Metnos «skill» and «backend» answer two different
questions. The backend says HOW a
verb_object runs against a service. The skill says
WHETHER and WHICH capabilities are unlocked, trusted and
shipped. Conflating them leads to splitting executors apart; keeping them
separate is what lets you add a provider without touching the planner.
| Backend / provider | Skill | |
|---|---|---|
| Answers | HOW I run verb_object against a service | WHETHER / WHICH capabilities are unlocked, trusted, shipped |
| Axis | object × provider | external dependency (credential / backend / hardware) |
| Visibility | invisible to the LLM, resolved from configuration by the backend resolver | user-facing (enable / disable, dormancy, sandbox, packaging) |
The two axes touch only when a skill has a single provider: in
that case github, geo, mail are in
practice the user-facing name of that provider's backend. They
diverge as soon as a skill has several backends (a calendar is
served over local ICS, Google or CalDAV) or has no
external provider at all (photos, the core). The map is
many-to-many: one provider (Google Workspace) serves several
skills — mail, calendar, drive, sheets, docs.
find_issues.
«Skill» ended up meaning three different things: a classification of
executors already in the repository, real packages in the repository, and
imported third-party code. The tier field unifies them under a
single model. It lives in skills_catalog.py (function
skill_tier()) and in the SKILL.md front matter.
| Tier | What it is | Trust | Where it lives | Activation |
|---|---|---|---|---|
| 1 — core | planner and engine, local files, dirs, processes, time, local scheduler, persons, credentials, signatures, proposals, the scratchpad helpers (filter / sort / group / compute / describe / extract) | maximum | repository, signed, multilingual | always on |
| 2 — first_party | capabilities shipped with Metnos at the same standard as the core, but dormant until the dependency is present: github, photos, mail, web, geo, calendar, frontier, Google Workspace | maximum | repository (versioned bundle + signed executors) | automatic, dormant if not configured |
| 3 — imported | third-party code, not audited nor vendored (from agentskills.io and the like) | low | user data, with tracked provenance | opt-in, sandbox + 7-layer net |
Tomorrow GitLab might arrive alongside GitHub. The question is: how do you add a second provider for the same issues? Two paths.
Generate find_issues_github and
find_issues_gitlab and let the planner choose. This works where a
frontier model sits at the center and disambiguates by reading prose. In Metnos
the planner is a local model, which faced with two nearly
identical names develops a provider bias:
it always picks the same one, or gets it wrong. Splitting
breaks routing.
find_issues, read_issues, find_pulls.
The object (issues, pulls) is the stable
abstraction.runtime/backends/{issues,pulls}/{github,gitlab}.py.github skill (PAT)
and a gitlab skill (token), with independent dormancy —
because the credential is the boundary where granularity is needed
(«enable gitlab» without touching github)._github / _gitlab
stays for the user's explicit pin («open the issue
on gitlab»); the default is agnostic.+1 backend file,
+1 skill (if it introduces a new credential),
0 new executors. It is the path calendar and files have already
taken.
find_issues_github + find_issues_gitlab confuse the local planner). On the right the abstracted state: a single find_issues, the resolver, two backends. The arrow is promotion: refactoring with a one-off frontier, then human review.*_github executors carry the provider in the name. It
works while the provider stays unique; the moment a second one arrives it must
be abstracted. General rule: a provider that plausibly will have siblings
(github→gitlab/gitea, IMAP mail, calendar) should be abstracted from the
start; one that will stay unique may remain baked-in.
In the multi-provider model the skill does not turn the executor on and off: it turns the backend on and off. This changes who sees what.
find_issues is available if
at least one of its backends is enabled and configured; it goes
dormant only when all of them are off (an AND across all
backends).find_issues — it only changes routing. Disable one of the
two: the other keeps serving. Disable both: the executor goes dormant.
Today gating is by pattern on the executor name
(skills_catalog): it works only while the provider is unique. The
new piece to build is a skill→backend map plus the rule
«executor dormant = AND over all its backends being off». It is the
first brick: needed as soon as the first multi-backend executor exists.
Merging a baked-in, mono-provider skill (*_github) into
find_issues + backends is an extraordinary
operation. A one-off frontier LLM is allowed ONLY for the
refactoring; detection and apply stay deterministic
and human-gated — auto-signing frontier-generated code would break the
trust model.
| Phase | Who | What |
|---|---|---|
| WHEN (trigger) | deterministic | on the 2nd provider's arrival — typically the import of a skill that maps onto an object already covered only by provider-suffixed executors (*_github, recognized by the naming authority). Object overlap + provider mismatch → «promotion candidate». No LLM needed to spot it. |
| WHERE (seam) | reused infrastructure | detection in the importer admission layer; refactoring via an escalation to a frontier model in agentic mode; landing in the admin changes page as a change of kind promote_provider, no auto-apply; apply writes the files, re-signs (§7.10), registers skill↔backend and per-backend gating (§4). |
| HOW (pipeline) | frontier only in the middle | detect (det.) → the frontier generates {canonical executor + backends/<obj>/{p1,p2}.py + skill→backend map} → the change is proposed → human review → apply + sign + verify with the 7-layer admission + smoke. |
To see why Metnos separates skills and backends while the others fuse them, look at who sits at the center choosing the provider.
| Hermes / drop-in skills | Claude-Code + MCP family | Metnos | |
|---|---|---|---|
| who picks the provider | frontier (reads SKILL.md, disambiguates) | frontier (reads tool descriptions) | medium local model (Gemma): has a bias on the provider |
| skill vs backend | fused (skill = folder of scripts = provider) | fused (tool / MCP server = provider) | separate (backend = execution, skill = activation) |
| multi-provider | parallel skills per provider, the agent chooses | parallel tools / servers, the model chooses | one canonical executor + one backend per provider (resolver) |
| credential / dormancy | required_credential_files + setup | implicit (server connected or not) | formalized dormancy |
| trust | runs the skill's code with its own privileges | runs tools / servers | closed vocabulary + 7-layer sandbox |
| To understand… | Read |
|---|---|
| how a third-party skill is imported and turned into signed executors | skill importer |
| the layered sandbox imported executors run in | sandbox |
| the checks before every risky action | vaglio |
| what an executor is and how it is built | executor |
Metnos — skill ↔ backend, two orthogonal axes