← Indice documentazione Microprogettazione › synt

Metnos

synt — come nasce e matura il pool degli executor
Allineata con la triade canonica
executor, mnest, mnestoma.

Pubblico: chi implementa il loop reattivo, l'omeostasi notturna,
i gate di approvazione e i controlli di costo.
Lettura: 18 minuti.
Filtri di qualità e short-circuit. La cascata introvertiva applica filtri deterministici prima di proporre: nomi fuori dal vocabolario chiuso rifiutati, args di pipeline saltati, soglia minima di utilizzi sotto la quale una ricorrenza è solo rumore. La richiesta esplicita di sintesi (handle_synth_request) verifica prima se l'intento è già coperto dal catalogo o re-indirizzabile a un executor canonico (es. list_processesget_processes), risparmiando l'intera pipeline. Un garbage collector sposta in un'area temporanea gli executor sintetizzati che collidono con quelli scritti a mano, senza cancellare.

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).

Una terza sorgente di proto-mnest, dal fast-path. D esiste un terzo canale di innesco, non sostitutivo ma complementare: il job notturno multi_tool_promote legge le pipeline memoizzate nel fast-path L2 e, quando una sequenza supera i 50 utilizzi, crea un proto-mnest in mnestoma con la firma desiderata (catena di executor + segnaposti + canonical query). Da quel momento la pipeline diventa candidata alla sintesi vera e propria: un nuovo executor unificato che incorpora tutta la sequenza, chiamato di solito <verbo_ultimo>_<oggetto_primo>. Il bridge non duplica lavoro: L2 cattura il volume basso (3–50 utilizzi, replay deterministico), L3 prende il sopravvento quando il pattern è stabilmente ricorrente.

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 multistage 5 stadi — locale €0 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 multistage a cinque stadi LLM (naming, signature, test, description, code) che produce un nuovo executor firmato (vedi §4.2.3).€0 (locale)
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 multistage a cinque stadi descritta in §4.2.3.

Generare costa: tipicamente 1–2 chiamate al tier wise + il tempo umano di approvazione. Il fatto che venga dopo comporre non è estetica: è risparmio reale.

Soglia di qualità del tier wise. Lo stadio codice (stadio 5 della pipeline multistage di §4.2.3) richiede un LLM al livello di Qwen 3.6 35B-A3B o superiore. Modelli più più piccoli producono codice fragile e hanno alta probabilità di violare la convenzione. Gli stadi procedurali (1-4: naming, signature, test, description) usano il tier middle, sufficiente per lookup nel vocabolario chiuso e formati fissi. La regola e' codificata nel tier resolver (runtime/llm_router.py) come quality floor: se l'utente non ha un wise locale di livello, deve configurare un provider esterno (Anthropic, OpenAI, ecc.). Errore esplicito al boot, mai degradazione silenziosa al fast.

4.2.1 Misure reali sul POC

Sintesi end-to-end via tier wise = Qwen 3.6 35B-A3B locale (Q4_K_M su llama-server). Caso: format_json (formatta una stringa JSON con indentazione leggibile).

StadioTempoNote
1 Pattern detect~0 (caller)chi chiama react ha già il proto-mnest in mano.
2+3 Spec + Scheletro~36suna sola chiamata LLM tier=wise via tool-use propose_executor; ~640 token in, ~1700 token out.
4 Profilo~msderivato da AST + import whitelist; profilo "pure" se nessun I/O esterno.
5 Birth-test livello 2~28s + ~1s/testseconda chiamata LLM produce 3-5 test dichiarativi; runner esegue ognuno via subprocess.
6 Approvalumanooggi CLI (synt approve|reject <id>); UX HTML rimandata.
7 Firma + installazione~msEd25519 via runtime/sign.py, executor copiato in executors/<name>/.

Latenza wall totale dello stadio LLM: ~64s per il primo executor non banale. È tempo che il dialog manager comunica all'utente come "sto costruendo un nuovo componente per estendere le mie capacità": una volta firmato, ogni successivo riuso e' pure code execution (~ms).

Semplificazione. Stadi 2 (Specifica) e 3 (Scheletro) sono unificati in una sola chiamata LLM con tool-use propose_executor. Il doc canonico li tiene separati perché in futuro introdurremo la UX di amend tra Specifica e Scheletro: Roberto potrà rivedere/correggere la spec prima della generazione del codice. Il tool unificato del POC e' una semplificazione esplicita, non una rinuncia al disegno.

4.2.2 Repertorio dei prompt provider-specifici

Ogni famiglia di modelli ha sue idiosincrasie scoperte solo con l'uso. Synt mantiene un repertorio di addendum di prompt applicati automaticamente agli stadi 2/3 quando il chiamante segnala for_code=True. Tre regole di stile, ratificate dopo i primi confronti reali:

Il repertorio vive in runtime/prompts.toml (default bundled) ed e' sovrascrivibile da ~/.config/metnos/prompts.toml (override utente). Schema TOML:

[[hint]]
provider = "anthropic"
model_pattern = "claude-*" # glob su prov.model
use_case = "code_gen"
text = "\\n\\nVincoli: codice fedele alla spec. Regex semplice. Niente lookbehind/lookahead."

[[hint]]
provider = "llamacpp"
model_pattern = "qwen*"
use_case = "code_gen"
text = "\\n\\nVincoli: raw string r'...' con UN backslash. Niente triple-quote docstring."

[[hint]]
provider = "openai"
model_pattern = "gpt-*"
use_case = "code_gen"
text = "\\n\\nVincoli: compila python_code per intero (def invoke + def main). Mai vuoto."

Il primo match per (provider, model_pattern, use_case) vince. Quando arriva un nuovo modello (Gemma 5, Claude Opus 4.7, GPT-5, …) si aggiunge una entry sulla base di quello che si osserva nelle prime sintesi. Il system prompt di base e gli executor non cambiano: l'unica leva che si tocca e' il repertorio.

4.2.3 Pipeline multistage

La risposta architetturale è multistage: cinque stadi piccoli, ognuno con prompt focalizzato sul proprio compito, ordinati dal piu' procedurale (lookup nel vocabolario chiuso) al piu' creativo (codice). Lo skeleton del manifest si riempie progressivamente, e ogni stadio vede SOLO la fetta che gli serve — niente blob cumulativo.

StadioTipoTier LLMOutput
1. Naming + classificationproceduralemiddle (Qwen 3.6 35B-A3B think=true)nome {azione}_{oggetto}[_qualifier] dal vocabolario chiuso (23 azioni, 22 oggetti) + revertible/critical/target_kind. Se nessuna combinazione calza: rejection esplicita con motivo.
2. Signatureproceduralemiddleargs_schema (JSON Schema), capabilities (set chiuso), reverse_pattern dal catalogo deterministico (runtime/reverse_patterns.py).
3. Birth testsproceduralemiddle4-6 test in formato fisso (setup/input/expect/teardown), almeno: caso felice, lista vuota, args invalidi, edge dominio.
4. Description + affinitycreativomiddledescription LLM-readable strutturata in quattro capitoli (SCOPO: / PATTERN: / NON: / OUT:). Il proposer mostra al planner solo la testa (fino a OUT: escluso). + 6-10 keyword di affinity per il composer.
5. Codicecreativo + proceduralewise (Qwen 3.6 35B-A3B think=true)<name>.py Python con def invoke, conformità alle convenzioni del runtime (runtime/messages.py, runtime/platform_policy.py, helpers).

Il vocabolario chiuso compare SOLO nel prompt di stage 1; gli stadi successivi ricevono il name già deciso e lavorano nei loro confini. La descrizione del manifest non la scrive il developer ma l'LLM dello stage 4 dedicato, nel formato a quattro capitoli: SCOPO: (cosa fa), PATTERN: (forma di chiamata canonica), NON: (anti-pattern e disambiguazione), OUT: (forma output). Il proposer tronca la description alla testa (fino a OUT:) per risparmiare budget di contesto.

Risultati misurati sui 35 query del dataset di stress: stadio 1 (naming + classification) raggiunge l'88.6 % (31/35) sul prompt bilingue. Le 4 escalation residue (resize, query SQL, validate YAML, crack) sono rejection esplicite per verbi semanticamente fuori vocabolario chiuso, non errori di sintesi: il vocabolario va eventualmente esteso solo se quei pattern ricorrono in casi reali («synonyms before vocabulary»).

Tempo di sintesi. Una sintesi end-to-end via multistage costa fra 140 e 160 secondi wall-clock con il provider locale Qwen 3.6 35B-A3B (zero costo di rete). Ripartizione media osservata: stadio 1 ~13 s, stadio 2 ~25 s, stadio 3 ~35 s, stadio 4 ~25 s, stadio 5 ~45 s. I 5 stadi si fanno in serie perche' ognuno dipende dallo skeleton arricchito dal precedente; il parallelismo rompe la regola «context minimo per stadio». Una ricostruzione one-shot al frontier resta la soglia ultima da non superare: mai usare wise online quando il locale arriva.

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 include 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 multistage a 5 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": ["read_files", "read_files_pdf", "classify_entries", "move_files"],
 "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 del primo disegno. Costoso e inquina il pool: ogni proto-mnest produce un nuovo executor anche quando una catena di executor esistenti basterebbe. Sostituito dalla cascata.
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 multistage 5 stadi al cap. 7 / §4.2.3.
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 canonica.