skill ↔ backend — due assi ortogonali
In Metnos «skill» e «backend» rispondono a due domande
diverse. Il backend dice COME si esegue un
verbo_oggetto contro un servizio. La skill dice
SE e QUALI capacità sono sbloccate, fidate e spedite.
Confonderli porta a sdoppiare gli executor; tenerli separati è quel che
permette di aggiungere un provider senza toccare il pianificatore.
| Backend / provider | Skill | |
|---|---|---|
| Risponde a | COME eseguo verbo_oggetto contro un servizio | SE / QUALI capacità sono sbloccate, fidate, spedite |
| Asse | oggetto × provider | dipendenza esterna (credenziale / backend / hardware) |
| Visibilità | invisibile all'LLM, risolto da configurazione dal resolver dei backend | visibile all'utente (abilita / disabilita, dormienza, recinto, impacchettamento) |
I due assi si toccano solo quando una skill ha un solo
provider: in quel caso github, geo, mail
sono di fatto il nome user-facing del backend di quel provider.
Divergono appena la skill ha più backend (il calendario
si serve su ICS locale, Google o CalDAV) o non ha
alcun provider esterno (le foto, il nucleo). La mappa è
molti-a-molti: un solo provider (Google Workspace) serve più
skill — posta, calendario, drive, fogli, documenti.
find_issues.
«Skill» ha finito per indicare tre cose diverse: una
classificazione di executor già nel repository, dei pacchetti veri in
repository, e codice di terzi importato. Il campo tier le unifica
sotto un modello solo. Vive in skills_catalog.py (funzione
skill_tier()) e nell'intestazione di SKILL.md.
| Tier | Cos'è | Fiducia | Dove vive | Attivazione |
|---|---|---|---|---|
| 1 — core | planner ed engine, file locali, cartelle, processi, tempo, scheduler locale, persone, credenziali, firme, proposte, gli helper dello scratchpad (filtra / ordina / raggruppa / calcola / descrivi / estrai) | massima | repository, firmato, multilingua | sempre attiva |
| 2 — first_party | capacità spedite con Metnos allo stesso standard del nucleo, ma dormienti finché manca la dipendenza: github, foto, posta, web, geo, calendario, archivio dati generico (sqldatabase), frontier, Google Workspace | massima | repository (pacchetto versionato + executor firmati) | automatica, dormiente se non configurata |
| 3 — imported | codice di terzi non auditato né vendorizzato (da agentskills.io e simili) | bassa | dati utente, con provenienza tracciata | opt-in, recinto + rete a 7 strati |
Domani potrebbe arrivare GitLab accanto a GitHub. La domanda è: come si aggiunge un secondo provider per le stesse issue? Due strade.
Generare find_issues_github e find_issues_gitlab
e lasciare che il pianificatore scelga. Funziona dove al centro c'è un
modello frontier che disambigua leggendo la prosa. In Metnos il pianificatore
è un modello locale, che davanti a due nomi quasi
identici sviluppa un pregiudizio sul provider:
sceglie sempre lo stesso, o sbaglia. Lo
sdoppiamento rompe il routing.
find_issues, read_issues, find_pulls.
L'oggetto (issues, pulls) è l'astrazione
stabile.runtime/backends/{issues,pulls}/{github,gitlab}.py.github (PAT) e
skill gitlab (token), con dormienza indipendente — perché
la credenziale è il confine dove serve granularità («abilita
gitlab» senza toccare github)._github / _gitlab
resta per il pin esplicito dell'utente («apri la issue
su gitlab»); il default è agnostico.+1 file backend,
+1 skill (se introduce una credenziale nuova),
0 nuovi executor. È il percorso che calendario e file
hanno già preso.
sqldatabase.
La forma astratta non è una promessa per il futuro: la skill
sqldatabase la incarna oggi. Offre tre executor canonici
— find_entries, write_entries,
delete_entries — che leggono, scrivono ed eliminano record
in archivi nominati (find_entries(store="spese", …)),
senza che la query sappia mai dove i dati vivono davvero. Sotto, due backend
intercambiabili sulla stessa base comune (SqlDatabaseBackend): uno
persistente su SQLite, uno effimero in memoria; un domani PostgreSQL si aggiunge
come sorella, senza toccare gli executor. Quale backend serva un dato archivio
si decide in un registro, alla dichiarazione dello store, mai
dalla richiesta dell’utente. La skill resta dormiente
finché nessuno store è registrato.
find_issues_github + find_issues_gitlab confondono il planner locale). A destra lo stato astratto: un solo find_issues, il resolver, due backend. La freccia è la promozione: refactoring con frontier una-tantum, poi vaglio umano. GitHub è attualmente nello stato «cotto».*_github portano il provider nel nome. Funziona
finché il provider resta unico; appena ne arriva un secondo va astratto.
Regola generale: un provider che plausibilmente avrà fratelli
(github→gitlab/gitea, posta IMAP, calendario) va astratto da subito; uno che
resterà unico può restare cotto.
Nel modello a più provider la skill non accende e spegne l'executor: accende e spegne il backend. Questo cambia chi vede cosa.
find_issues è disponibile se
almeno uno dei suoi backend è abilitato e configurato;
diventa dormiente solo se sono spenti tutti (un AND su tutti i
backend).find_issues — cambia solo il routing. Disabilitare uno
dei due: l'altro continua a servire. Disabilitarli entrambi: l'executor diventa
dormiente.
Oggi il gating è per pattern sul nome dell'executor
(skills_catalog): funziona solo finché il provider è
unico. Il pezzo nuovo da costruire è una mappa
skill→backend più la regola «executor dormiente = AND su
tutti i suoi backend spenti». È il primo mattone: serve appena
esiste il primo executor a più backend.
Fondere una skill mono-provider cotta (*_github) in
find_issues + backend è un'operazione
straordinaria. È ammesso un LLM frontier
una-tantum SOLO per il refactoring; rilevamento e
applicazione restano deterministici e con vaglio umano — firmare
in automatico del codice generato da un frontier romperebbe la fiducia.
| Fase | Chi | Cosa |
|---|---|---|
| QUANDO (trigger) | deterministico | all'arrivo del 2° provider — tipicamente l'import di una skill che mappa su un oggetto già coperto solo da executor provider-suffissati (*_github, riconosciuti dall'autorità di naming). Sovrapposizione di oggetto + provider diverso → «candidato a promozione». Nessun LLM per accorgersene. |
| DOVE (seam) | infrastruttura riusata | rilevamento nel layer di ammissione dell'importer; refactoring tramite l'escalation a un modello frontier in modo agentico; atterraggio nella pagina admin delle modifiche come modifica di tipo promote_provider, niente auto-apply; applicazione che scrive i file, ri-firma (§7.10), registra skill↔backend e gating per-backend (§4). |
| COME (pipeline) | frontier solo in mezzo | rileva (det.) → il frontier genera {executor canonico + backends/<obj>/{p1,p2}.py + mappa skill→backend} → la modifica viene proposta → vaglio umano → applica + firma + verifica con i 7 strati di ammissione + smoke. |
Per capire perché Metnos separa skill e backend mentre gli altri li fondono, conviene guardare chi sta al centro a scegliere il provider.
| Hermes / skill drop-in | Claude-Code + famiglia MCP | Metnos | |
|---|---|---|---|
| chi sceglie il provider | frontier (legge SKILL.md, disambigua) | frontier (legge le descrizioni dei tool) | modello locale medio (Qwen): ha un pregiudizio sul provider |
| skill vs backend | fusi (skill = cartella di script = provider) | fusi (tool / server MCP = provider) | separati (backend = esecuzione, skill = attivazione) |
| multi-provider | skill parallele per provider, l'agente sceglie | tool / server paralleli, il modello sceglie | un executor canonico + un backend per provider (resolver) |
| credenziale / dormienza | required_credential_files + setup | implicita (server connesso o no) | dormienza formalizzata |
| fiducia | esegue il codice della skill coi propri privilegi | esegue tool / server | vocabolario chiuso + recinto a 7 strati |
| Per capire… | Leggi |
|---|---|
| come si importa una skill di terzi e la si trasforma in executor firmati | skill importer |
| il recinto a strati in cui gli executor importati eseguono | sandbox |
| i controlli prima di ogni azione rischiosa | vaglio |
| cos'è un executor e com'è fatto | executor |
Metnos — skill ↔ backend, due assi ortogonali