✓ Microprogettazione v1.1 — APPROVATO (25 aprile 2026). Doc canonico approvato dopo simulazione end-to-end del 25/4/2026, allineato con l'Architettura v1.1 e con i due dialoghi galileiani. Riferimento normativo per l'implementazione. Sostituisce il vecchio synthesizer.html (deprecato).
← Indice documentazione Microprogettazione › synt

Metnos

synt — come nasce e matura il pool degli executor
Microprogettazione v1.1 — 25 aprile 2026
Allineata con Architettura v1.1 (cap. 9 e 11) e con la triade canonica
executor, mnest, mnestoma.
Sostituisce synthesizer.html (deprecato).

Pubblico: chi implementerà il loop reattivo, l'omeostasi notturna,
i gate di approvazione e i controlli di costo.
Lettura: 18 minuti.

Indice

  1. Scopo e confini
  2. Cos'è synt
  3. La cascata: visione d'insieme
  4. Strategie reattive: comporre, generare
  5. Strategie introvertive: fondere, generalizzare, specializzare
  6. Pesatura: il punteggio R esteso
  7. Il telos di non-rinuncia
  8. Approvazione umana, budget, abbandono
  9. Contratto Python
  10. Audit e osservabilità
  11. Alternative considerate
  12. Test di conformità
  13. Domande aperte

1. Scopo e confini

Questo documento definisce cosa fa synt: il processo che, di fronte a una richiesta utente o a un pattern emerso nel mnestoma, fa crescere e maturare il pool degli executor di Metnos. Non è un singolo loop: è una cascata di strategie ordinate per costo crescente, che onora il telos «coltivare gli strumenti» senza sprecare chiamate frontier e senza inquinare la libreria.

Confini

Il documento copre:

Non copre, e demanda altrove:

2. Cos'è synt

synt è il processo (non un agente, non un oggetto) che ha un solo compito: far esistere ciò che il pool non sa ancora fare. Non scrive da zero ogni volta, non scrive solo da zero, e a volte non scrive affatto. La sua intelligenza sta nel sapere quando applicare quale strategia.

synt ≠ LLM frontier. È un errore comune pensare che «sintesi» significhi «chiamare un modello costoso e farsi scrivere codice». Nella maggior parte dei casi, sintetizzare significa orchestrare ciò che già esiste: una catena di executor attivi che chiude un proto-mnest. La generazione vera e propria è l'eccezione documentata, non il default.

Trigger

synt entra in azione in due modi distinti:

ModoInnescoTempo
Reattivo Il gateway, durante un turno utente, registra un proto-mnest verso un executor che non esiste; oppure il pianificatore non trova nessun executor che soddisfi la richiesta. Sincrono al turno (l'utente sta aspettando una risposta).
Introvertivo L'ager nottetempo scorre il mnestoma e il pool firmato; trova proto-mnest ricorrenti, executor con tracce sovrapposte, famiglie di specializzati con stessa forma. Asincrono, in omeostasi (notte, pause, basso carico).

Le due modalità condividono la stessa cascata di strategie ma si differenziano nei tempi (sincrono vs asincrono) e nel tono (rispondere vs proporre).

3. La cascata: visione d'insieme

La cascata di synt strategie ordinate per costo crescente REATTIVO turno utente proto-mnest (richiesta non chiusa) 1. Comporre catena nel pool — zero LLM frontier 2. Generare pipeline 7 stadi — ~1 € di frontier Abbandono motivato e tracciato se trova catena → orchestrato se compose vuota → nuovo executor se max retry o budget INTROVERTIVO omeostasi notturna mnestoma + pool (scorrimento periodico) 3. Fondere A + B con tracce sovrapposte 4. Generalizzare N specializzati → uno parametrico 5. Specializzare caso caldo con beneficio misurato Proposte in batch → gate umano (approval_ux) Roberto vede dossier con punteggio R, può approvare/emendare/scartare Principio: ogni strategia è una proposta. Niente entra nel pool senza firma e senza gate umano.
Figura 1 — La cascata di synt. Riga superiore: due strategie reattive che rispondono a un proto-mnest dentro il turno. Riga inferiore: tre strategie introvertive che curano il pool nottetempo e arrivano a Roberto in batch.

Le cinque strategie a colpo d'occhio

#StrategiaTempoCosa fa, in una rigaCosto frontier
1ComporrereattivoCerca una catena di executor attivi che chiude il proto-mnest.0
2GenerarereattivoPipeline a sette stadi che produce un nuovo executor firmato.~1 €
3FondereintrovertivoUnisce due executor con tracce sovrapposte e profili compatibili.~0.5 €
4GeneralizzareintrovertivoDa N specializzati con stessa forma deriva un executor parametrico.~1 €
5SpecializzareintrovertivoDa uno generale deriva una versione mirata a un caso caldo.~0.5 €
Due assi, non una lista lineare. Le strategie si distinguono su due dimensioni indipendenti: quando (reattivo / introvertivo) e come (riuso / creazione / trasformazione). Comporre è riuso puro; generare è creazione; fondere/generalizzare/specializzare sono trasformazioni del pool. La numerazione qui è un ordine espositivo, non una priorità assoluta.

4. Strategie reattive: comporre, generare

4.1 Comporre

La strategia di base. Quando un proto-mnest indica un buco operativo, synt prima guarda nel pool firmato. La domanda è: esiste una catena A → B → C che, se eseguita in sequenza, copre il bisogno? Se sì, synt non scrive codice nuovo: propone l'orchestrato.

Come si cerca la catena

Il mnestoma è un grafo diretto fra executor. Trovare una catena che collega l'input richiesto all'output desiderato è una visita guidata sul grafo. Tre criteri ordinano le candidate:

Cosa lascia una composizione riuscita

Quando synt risolve via composizione, registra nel mnestoma un proto-mnest verso la composizione stessa: un nodo virtuale che dice «ho appena usato A→B→C come fosse un singolo executor di nome X». Se questo proto-mnest ricorre, diventa candidato di generalizzazione (cap. 5): un executor unico che incorpora la catena. La composizione è quindi una palestra di generalizzazione: ogni catena ripetuta è un suggerimento per il futuro.

DECISIONE v1: la lunghezza massima di una catena di composizione è 5 hop. Oltre, l'orchestrato diventa illeggibile e il rischio di errori a cascata supera il vantaggio del riuso. Synt salta a generare. La soglia è tunabile.

4.2 Generare

Quando comporre non basta — perché non esiste catena, perché è troppo lunga, perché un anello mancante è cruciale — synt entra nella pipeline a sette stadi descritta in executor.html cap. 7:

  1. Pattern detect: l'ager conferma la ricorrenza del proto-mnest.
  2. Specifica: una chiamata LLM frontier produce una specifica testuale (purpose, I/O, errori).
  3. Scheletro: seconda chiamata frontier per il main.py e lo schema.json.
  4. Profilo: synt calcola il profilo di sandbox più stretto compatibile con i test.
  5. Test di nascita: tutti devono passare con quel profilo.
  6. Approvazione umana: non bypassabile, in nessun livello di autonomy.
  7. Firma e installazione: HMAC, profile lock, executor entra nel pool.

Generare costa: tipicamente 1–2 chiamate frontier (~0.8–1.3 €) + il tempo umano di approvazione. Il fatto che venga dopo comporre non è estetica: è risparmio reale.

5. Strategie introvertive: fondere, generalizzare, specializzare

Le strategie introvertive non rispondono a una richiesta puntuale: rispondono al bisogno strutturale di tenere piccolo, coerente e riusabile il pool. Girano nottetempo come parte dell'omeostasi dell'ager (vedi Architettura cap. 10), con basso uso di CPU e LLM economici (tier local-fast).

5.1 Fondere

Quando due executor hanno tracce sovrapposte (peso dei mnest che li collegano > soglia) e profili di sandbox compatibili, synt propone una fusione: un nuovo executor che li copre entrambi. Lo stato fuso nel lifecycle dell'executor (cap. 6) è previsto proprio per questo: i due originali restano caricati finché ci sono mnest residui, poi vengono archiviati.

Innesco tipico: due executor che fanno cose simili sotto nomi diversi (archive_pdf e store_pdf), magari nati in momenti diversi senza che synt li avesse correlati alla nascita.

5.2 Generalizzare

Quando N executor specializzati hanno la stessa forma (stesso schema I/O salvo una dimensione, stesso profilo di sandbox salvo un parametro), e quando i proto-mnest puntano alla loro famiglia su dimensioni nuove, synt propone un executor parametrico. La nuova versione prende come argomento esplicito quello che prima era ricodificato N volte.

Esempio canonico: order_image_file, order_audio_file, order_doc_file — tutti riordinano file per data — vengono proposti come order_file con argomento file_kind. Una volta firmato il generalizzato, i tre specializzati vanno in stato superseded e progressivamente in archiviato.

Generalizzazione non è refactoring opportunistico. La soglia per proporre una generalizzazione richiede: (a) almeno tre specializzati con shape coerente; (b) almeno un proto-mnest non coperto sulla stessa famiglia; (c) assenza di divergenza nei profili di sandbox che renderebbe il parametrico più permissivo della somma. Se (c) fallisce, la proposta è sospesa: meglio tre executor stretti che uno largo.

5.3 Specializzare

La strategia inversa, ed è la più rara. Da un executor generale, quando una sua invocazione su un caso specifico ricorre con altissima frequenza e ha un profilo di costo o latenza scomodo, synt propone una versione specializzata. La specializzazione vive accanto al generale, non lo sostituisce: il routing diventa «se argomento X ≡ caso caldo, usa lo specializzato; altrimenti il generale».

Si applica solo se c'è un beneficio misurato: riduzione di latenza osservata, riduzione di costo, oppure semplificazione del profilo di sandbox. Non si specializza per ottimizzazione preventiva.

5.4 Confronto fra le tre

StrategiaDirezioneEffetto sul poolTrigger
FondereN → 1riduce il numero di executortracce sovrapposte, profili compatibili
GeneralizzareN → 1 (parametrico)riduce e copre nuove dimensionispecializzati con shape coerente + proto-mnest sulla famiglia
Specializzare1 → 2 (gen + spec)aggiunge un executorcaso caldo con beneficio misurato

6. Pesatura: il punteggio R esteso

Ogni proposta di sintesi — qualunque strategia — produce un punteggio R ∈ [0, 1] che synt calcola sui dati osservati e che Roberto vede nel dossier di approvazione. La formula consolida quella della microprogettazione precedente (synthesizer.html §7.1) e aggiunge una componente strategy_cost che premia le strategie più economiche:

R = 0.35 · det_pass_rate           # frazione test deterministici verdi
  + 0.20 · judge_score             # LLM-as-judge (local-fast) su rubrica costituzionale
  + 0.15 · cost_ratio              # clip(1 - cost_effettivo / stima, 0, 1)
  − 0.10 · similarity_penalty      # cosine-sim embedding con pool esistente > 0.85
  + 0.10 · coverage_bonus          # bonus se copre proto-mnest non coperti
  + 0.10 · strategy_cost_bonus     # 1 per comporre, 0.6 per fondere/specializzare,
                                       # 0.4 per generalizzare, 0 per generare

gate_threshold = 0.65                # DECISIONE v1

Il strategy_cost_bonus è la novità: spinge synt verso le strategie meno costose a parità di qualità. Una composizione che chiude un proto-mnest con punteggio simile a una generazione vince per il bonus di strategia, com'è corretto.

DECISIONE v1 (taratura). I pesi sopra sono prima approssimazione, da rivalutare dopo 30 sintesi reali sul sistema. La regola di calibrazione: nessuna strategia deve avere bonus tale da farla vincere quando è strutturalmente sbagliata (es. una composizione lunga 7 hop che vince su una generazione corretta solo per il bonus). Se questo accade, abbassare il bonus.

7. Il telos di non-rinuncia

La cascata non è solo disciplina di costo: è il meccanismo che onora il telos «coltivare gli strumenti» (Architettura cap. 11): finché una richiesta utente è dentro la costituzione e dentro il budget, synt esaurisce le strategie disponibili prima di rispondere «non posso».

Il telos vive in TELOS.md nel workspace, accanto agli altri:

5. Coltivare gli strumenti: se ti chiedo qualcosa che il tuo pool non sa
   ancora fare, esaurisci le strategie di sintesi (comporre, generare,
   chiedere) prima di rispondere "non posso". Il fallimento è lecito,
   la rinuncia silenziosa no.

La differenza fra fallimento e rinuncia è tutta qui. Synt può arrivare alla conclusione che la richiesta non si può soddisfare nel budget, ma deve farlo dopo aver tentato la cascata, e deve spiegarlo: «ho provato a comporre con questi N executor, ho provato a generare con questa specifica, il test di nascita falliva sul caso X». Un fallimento motivato è un dato per il futuro; una rinuncia silenziosa è un buco nel mnestoma.

Non confondere non-rinuncia con testardaggine. Le soglie di abbandono restano valide: max 3 retry per stadio, sospensione di 24h per proto-mnest pattern-rifiutato, lock di 30 giorni per direzione interna rifiutata. La non-rinuncia è esaurire la cascata, non insistere all'infinito. Coraggio, non testardaggine.

8. Approvazione umana, budget, abbandono

8.1 Gate umano: sempre, ovunque

Indipendentemente dalla strategia, ogni proposta di synt che produce o modifica un executor passa per il gate umano (vedi approval_ux.html, da riscrivere). Il principio è quello del terzo dei sei principi dell'Architettura: niente sintesi senza filtro umano, in nessun livello di autonomy.

Una proposta di composizione è un caso a parte: synt non crea nessun nuovo artefatto firmato, solo orchestra l'esecuzione. Il gate umano in questo caso si applica al primo uso (Roberto vede «sto per chiamare A → B → C, va bene?») e poi viene rilassato in modo simmetrico al livello di autonomy: in Supervised ogni invocazione della catena chiede conferma; in Full, dopo le prime 5 esecuzioni pulite, la conferma viene saltata.

8.2 Budget

TettoDefaultComportamento al supero
Soft per richiesta2 €synt avvisa Roberto e chiede se proseguire.
Hard per richiesta5 €synt si ferma, registra abbandono per budget.
Hard al giorno20 €cap costituzionale (non superabile via config).

8.3 Stati di una richiesta di sintesi

StatoSignificatoTransizioni
composingCerca catena nel pool.composed (catena trovata) oppure generating.
composedCatena proposta, in attesa del gate utente.delivered oppure generating se Roberto rifiuta.
generatingPipeline 7 stadi in corso.born (executor firmato) oppure abandoned.
bornExecutor firmato, in pool.terminale.
abandonedTutte le strategie esaurite, fallimento motivato.terminale; lock di 24h sullo stesso scopo.
proposedStrategia introvertiva, in attesa di batch utente.born/fused/generalized/specialized oppure rejected.
rejectedRoberto ha respinto la proposta introvertiva.terminale; lock di 30 giorni sulla stessa direzione.

9. Contratto Python

from typing import Protocol, Literal
from dataclasses import dataclass

Strategy = Literal[
    "compose", "generate",
    "merge", "generalize", "specialize",
]

ProposalState = Literal[
    "composing", "composed",
    "generating", "born", "abandoned",
    "proposed", "rejected",
    "fused", "generalized", "specialized",
]

@dataclass(frozen=True)
class SynthRequest:
    """Una richiesta di sintesi, reattiva o introvertiva."""
    request_id:    str
    mode:          Literal["reactive", "introspective"]
    proto_mnest:   str | None       # id del proto-mnest che innesca (reattivo)
    target_intent: str              # NL: cosa serve fare
    budget_cents:  int              # tetto frontier consumabile
    capability_hint: list[str]      # capability già supposte

@dataclass(frozen=True)
class RewardBreakdown:
    det_pass_rate:        float    # [0,1]
    judge_score:          float    # [0,1]
    judge_reasoning:      str
    cost_ratio:           float    # [0,1]
    similarity_penalty:   float    # [0,1] (col segno negativo nella formula)
    coverage_bonus:       float    # [0,1]
    strategy_cost_bonus:  float    # [0,1] dipende da Strategy
    total:                float    # R aggregato

@dataclass
class SynthProposal:
    """Una proposta concreta che synt presenta all'utente."""
    request_id:   str
    strategy:     Strategy
    state:        ProposalState
    artefact:     dict             # catena (compose) o executor candidato (generate/…)
    reward:       RewardBreakdown
    cost_cents:   int              # consumo finora
    rationale:    str              # 2-3 righe NL: perché questa strategia, qui

class Synt(Protocol):
    async def react(self, req: SynthRequest) -> SynthProposal:
        """Cascata reattiva: prima compone, poi genera, poi abbandona."""
        ...

    async def homeostasis(self, lookback_days: int = 30) -> list[SynthProposal]:
        """Scorre mnestoma e pool; ritorna 0..N proposte introvertive in batch."""
        ...

    async def revise(
        self, request_id: str, feedback: str,
        target_strategy: Strategy | None = None,
    ) -> SynthProposal:
        """Riprende una proposta dopo feedback umano."""
        ...

# Errori (registrati e trasformati in stato, non propagati)
class StrategyExhaustedError(Exception): ...
class BudgetExceededError(Exception): ...
class PolicyVetoError(Exception): ...
class ConstitutionViolationError(Exception): ...

10. Audit e osservabilità

Ogni passo della cascata produce una riga JSONL in workspace/.audit/synt/YYYY-MM-DD.jsonl. Esempio per una composizione riuscita:

{
  "ts":            "2026-04-25T22:14:33Z",
  "request_id":    "01HX...",
  "mode":          "reactive",
  "proto_mnest":   "mnest_01HW...",
  "strategy":      "compose",
  "state":         "composed",
  "chain":         ["fs_read", "pdf_extract", "invoice_classify", "workspace_save"],
  "reward":        {"total": 0.78, "det_pass_rate": 0.95, "…": "…"},
  "cost_cents":    0,
  "duration_ms":   142
}

Tre invarianti dell'audit di synt, che il loader e il runtime garantiscono:

  1. Tracciabilità completa: per ogni request_id esiste l'intera sequenza di stati attraversati, fino al terminale.
  2. Decisioni motivate: ogni passaggio fra strategie include un rationale NL leggibile da Roberto a posteriori.
  3. Costo registrato: la somma dei cost_cents di una richiesta non supera mai il budget_cents dichiarato; la riga finale lo certifica.

11. Alternative considerate

AlternativaPerché scartata (o rimandata)
Solo generazione, niente cascataDefault originale di synthesizer.html v1.0. Costoso e inquina il pool: ogni proto-mnest produce un nuovo executor anche quando una catena di executor esistenti basterebbe. Sostituito dalla cascata il 25/4/2026.
Cascata estesa (anche cap. 5 reattivo)Fondere/generalizzare/specializzare nel turno utente moltiplica il rischio: tre proposte concorrenti in parallelo, gate umano sotto pressione. Restano introvertivi.
Generazione con N modelli in parallelo3× il costo per piccola riduzione di varianza. Eventuale in v2 se l'hit-rate della pipeline scende sotto il 60%.
Reward learning (RLHF) sui pesi di RRichiede dataset di feedback umano firmato; sovradimensionato per uso domestico. La taratura manuale + revisione semestrale è più trasparente.
Composizione con LLM (planner)Sostituisce la visita di grafo con una chiamata frontier che inventa la catena. Costoso e fragile: la visita è deterministica e ispezionabile, l'LLM no.
Auto-merge senza gateUna fusione che cambia profili di sandbox è un atto di sicurezza. Resta gated.

12. Test di conformità

InvarianteTest
La cascata reattiva tenta sempre comporre prima di generareIniezione di un proto-mnest risolvibile da catena nota → strategy = compose nel proposal, cost_cents = 0 in chiamate frontier.
Generare scatta solo se comporre fallisceProto-mnest senza catena possibile → transizione composing → generating in audit; compose non emesso come strategia finale.
Catena max 5 hopForzare 6 hop → visita rifiuta la catena, fallback a generare.
Gate umano non bypassabileMock di approval_ux che non viene chiamato → nessuno stato born/fused/… raggiunto.
Budget hard rispettatoRichiesta con budget 0.10 € → abbandono prima della seconda chiamata frontier; cost effettivo ≤ 0.10 €.
R reproducibileReplay con stesso seed → stesso R ± 0.01 (judge_score con tolleranza).
Strategy cost bonus monotonoPer stessa qualità (det_pass_rate, judge), R(compose) > R(merge) > R(specialize) > R(generalize) > R(generate).
Lock 24h post-abbandonoStesso target_intent entro 24h → SynthRequest rifiutata con stato abandoned immediato.
Lock 30 giorni post-rejected internoDirezione introvertiva rifiutata da Roberto → nessuna nuova proposta sulla stessa direzione per 30 giorni.
Audit completoPer ogni request_id con stato terminale, esistono in audit la riga iniziale e quella terminale, e tutti gli stati intermedi.
Coerenza profili in fusioneTentativo di fondere due executor con profili sandbox incompatibili (es. uno scrive su ~/Pictures/, l'altro no) → proposta sospesa con motivazione.

13. Domande aperte

  1. Casa dei tre passi introvertivi. Restano in synt.html, oppure migrano in un nuovo consolidator.html? La separazione potrebbe semplificare i contratti (synt = reattivo, consolidator = introvertivo) ma duplicherebbe parti di pesatura. Oggi: tutto qui. Aperta.
  2. Tempi dell'omeostasi. Notte fissa o adattiva al carico? Per Metnos su metnos-server singolo utente la notte basta; per scenari multi-sender va rivisto. Aperta.
  3. Composizioni rinforzate → promozione. Quante ricorrenze di una stessa catena triggerano una proposta di generalizzazione? Default tentativo: 5 in 30 giorni. Da calibrare.
  4. Visita del grafo per comporre. Algoritmo: breadth-first sul mnestoma con pruning per peso minimo? A* con euristica di copertura I/O? Decisione aperta — per ora BFS semplice.
  5. Revoca della firma. Quando Roberto rifiuta una proposta già born dopo n invocazioni, è un'archiviazione o una quarantena? Lifecycle dell'executor (cap. 6) ammette entrambi; criterio non ancora fissato.
  6. Synt remoto. In topologia con executor remoti (Architettura cap. 4), la cascata gira su metnos-server o può spawnare composizioni che attraversano server e laptop? Aperta; default conservativo: tutto su metnos-server, executor remoti chiamati come ogni altro.

canonico
executor
L'unità di codice che synt fa nascere o orchestra. Pipeline 7 stadi al cap. 7.
canonico
mnest
La traccia di co-attivazione che synt legge per decidere. Proto-mnest al cap. 8.
livello 1
Architettura, cap. 9
La cascata nel quadro d'insieme.
livello 1
Architettura, cap. 11
Telos e Vaglio: il telos di non-rinuncia.
indice
Microprogettazione
Tutti i doc.

Metnos — synt microprogettazione v1.1 — 2026-04-25
Doc canonico nuovo. Sostituisce synthesizer.html (deprecato).