← Indice documentazione Microprogettazione › neuron

myclaw

neuron — anatomia di una capacità auto-sintetizzata
Microprogettazione v1.0 — 22 aprile 2026
Primo documento dell'estensione «Neuroni e Memoria».
Reifica i concetti introdotti in Neuroni+Memoria v1.1 §2, §3, §10.

Pubblico: chi implementerà il loader, la firma, la quarantena. Lettura: 18 min.

Indice

  1. Scopo e confini
  2. Anatomia su disco
  3. Schema del manifest
  4. Il corpo: Protocol Tool
  5. Firma HMAC e ciclo di fiducia
  6. Journal: counter, esiti, sinapsi osservate
  7. Stati di vita
  8. Contratto Python
  9. Implementazione di default
  10. Alternative considerate
  11. Test di conformità
  12. Riferimenti

1. Scopo e confini

Un neurone è la materializzazione di una capacità che myclaw non aveva e che ha sintetizzato al bisogno. Questo documento definisce la sua anatomia: come è fatto, come vive, come è firmato. La pipeline di sintesi è in synthesizer.html; il grafo dei rapporti fra neuroni è in synapse.html.

Cosa copre

Cosa non copre

2. Anatomia su disco

Ogni neurone vive in una directory dedicata sotto workspace/neurons/<nome>/. La directory è self-contained: corpo, manifest, test, firma e journal stanno insieme. Per spostare o archiviare un neurone basta spostare la sua directory.

workspace/neurons/log-parser/
├── manifest.yaml           # schema dichiarativo (capability, quote, sinapsi)
├── body.py                 # il codice Python: funzione run(...)
├── test_birth.py           # test di nascita (deve passare in sandbox)
├── signature.hmac          # HMAC-SHA256 su manifest+body
├── journal.sqlite          # counter, esiti, sinapsi osservate
└── README.md               # opzionale: dettagli generati dal synthesizer
workspace/neurons/<nome>/ manifest.yaml nome, versione, scopo capability, quote sinapsi dichiarate body.py async def run(ctx, **kw) implementa Protocol Tool solo stdlib + capability test_birth.py 3+ casi golden deterministico timeout 30 s signature.hmac HMAC-SHA256(manifest+body+test) chiave: ~/.config/myclaw/neuron.key firma al momento dell'approvazione mismatch → quarantena automatica journal.sqlite invocations, outcomes, synapses append-only aggregato periodicamente fonte di verità per fitness e potatura
Figura 1 — Una directory-neurone. Manifest e body sono immutabili; la firma è calcolata su entrambi (più il test) e vive a fianco; solo il journal cresce nel tempo ed è fuori dal perimetro firmato.
Il journal è volutamente fuori dalla firma. Se includessimo i counter nel payload firmato, ogni invocazione invaliderebbe la firma. La firma protegge il codice, non il diario operativo.

3. Schema del manifest

# workspace/neurons/log-parser/manifest.yaml
name: log-parser                    # ^[a-z][a-z0-9_-]{2,31}$, univoco
version: 1.4.0                      # semver
created_at: 2026-04-22T21:11:00+02:00
created_by: synthesizer@myclaw      # o "manual" se scritto a mano
parent_trace: 7c3f...d21            # trace_id che ha innescato la sintesi

purpose: |
  Estrae errori e warning da log strutturati in JSONL,
  raggruppa per servizio e ritorna un riassunto.

# --- contratto di invocazione ---
entry: body:run                     # modulo:funzione
input_schema:                       # JSON Schema subset
  type: object
  required: [path]
  properties:
    path: { type: string, format: "local-readable-path" }
    since: { type: string, format: "date-time" }
output_schema:
  type: object
  required: [by_service]
  properties:
    by_service: { type: object }

# --- capability richieste (match esatto con policy.html §4) ---
capabilities:
  - fs-read:~/logs
  - fs-read:~/downloads/logs
  # no network, no shell, no fs-write

# --- quote al runtime ---
quotas:
  wall_time_s: 20
  cpu_time_s: 10
  ram_mb: 256
  max_stdout_kb: 512
  max_invocations_per_hour: 60

# --- sinapsi dichiarate (vincolo massimo; vedi synapse.html) ---
may_call:
  - summarizer          # può chiedere un riassunto LLM
  - fs-read             # tool base

# --- metadati per retrieval ---
tags: [log, observability, parsing]
embeddings_version: supra-embed-v1  # per invalidare l'index se cambia il modello

Campi obbligatori

CampoVincolo
nameslug, univoco nel workspace. Case-sensitive NO.
versionsemver stretto. Bump patch per fix, minor per estensione input compatibile, major per breaking.
purposeLinguaggio naturale, 1–4 frasi. Usato per retrieval semantico.
entryFormato modulo:funzione. Funzione DEVE essere async.
input_schema / output_schemaJSON Schema Draft 2020-12. Validato a ogni invocazione.
capabilitiesLista di capability. Ciascuna risolve rispetto al registro in policy §4. Vuota → neurone puramente computazionale (no I/O).
quotasLimiti applicati in sandbox. Default conservativi se omessi (15s wall, 128 MB ram).
may_callLista di neuroni/tool base che il corpo può invocare. Chiamate fuori da questa lista → errore UndeclaredSynapseError.
DECISIONE v1: input/output schema sono obbligatori e validati server-side (dentro il runtime, prima e dopo l'esecuzione). Il costo è trascurabile con jsonschema e ci protegge dai neuroni che "ritornano quello che capita".

4. Il corpo: Protocol Tool

Il corpo di un neurone implementa lo stesso Protocol Python dei tool nativi (tool.html §3). Non c'è distinzione a livello di runtime: un neurone è un tool che vive nel filesystem invece che in src/myclaw/tools/.

# workspace/neurons/log-parser/body.py
from myclaw.runtime import NeuronContext, ToolResult

async def run(ctx: NeuronContext, path: str, since: str | None = None) -> ToolResult:
    # ctx espone: ctx.open(path, "r"), ctx.call("summarizer", ...),
    # ctx.log, ctx.quota_remaining(). Niente import di sistema diretti.
    data = {}
    async with ctx.open(path, "r") as f:
        async for line in f:
            rec = ctx.json_loads(line)
            if since and rec["ts"] < since: continue
            if rec.get("level") in ("ERROR", "WARN"):
                data.setdefault(rec["service"], []).append(rec)
    return ToolResult(ok=True, value={"by_service": data})

Regole di scrittura

5. Firma HMAC e ciclo di fiducia

Algoritmo

payload = manifest.yaml_bytes + b"\n---\n"
        + body.py_bytes      + b"\n---\n"
        + test_birth.py_bytes

signature = HMAC-SHA256(key=neuron_signing_key, msg=payload)

# scritto come hex in signature.hmac, una sola riga, trailing newline.

Chiave di firma

Verifica ad ogni load

  1. All'avvio del gateway — e ad ogni caricamento a caldo — il loader ricalcola l'HMAC.
  2. Mismatch → neurone NON caricato, stato quarantena, evento audit neuron.signature.invalid.
  3. Match → neurone disponibile, stato attivo (o quello precedente).
Perché HMAC e non una firma asimmetrica? Lo scopo qui non è distribuzione (non pubblichiamo neuroni). È rilevare manomissioni locali: un processo malevolo che modifica body.py non ha la chiave, quindi la firma non torna, quindi al prossimo load il neurone è disattivato. HMAC è sufficiente, semplice, veloce. Se un domani dovessimo condividere neuroni fuori dal workspace, passeremo a firma asimmetrica (Ed25519) con ceremony di trust esplicita.

6. Journal: counter, esiti, sinapsi osservate

Il journal è uno SQLite per neurone. Tenere i diari separati evita contention e permette di spostare/archiviare una directory senza toccare un DB centrale. Il grafo globale — unione dei journal — è costruito dal componente synapse a partire da queste sorgenti.

-- workspace/neurons/<nome>/journal.sqlite

CREATE TABLE invocations (
    invocation_id TEXT PRIMARY KEY,
    trace_id      TEXT NOT NULL,
    started_at    DATETIME NOT NULL,
    duration_ms   INTEGER NOT NULL,
    outcome       TEXT NOT NULL,   -- success | error | timeout | quota_exceeded
    error_class   TEXT,
    input_hash    TEXT,            -- sha256 input normalizzato (privacy)
    goal_hash     TEXT,            -- sha256 dello scopo corrente (per Gap)
    gap_pre       REAL,            -- riempito dal caller
    gap_post      REAL,
    cost_cents    REAL DEFAULT 0.0
);

CREATE TABLE co_activations (
    trace_id   TEXT NOT NULL,
    peer_name  TEXT NOT NULL,      -- neurone chiamato nella stessa trace
    direction  TEXT NOT NULL,      -- 'calls' | 'called_by'
    observed_at DATETIME NOT NULL
);

CREATE INDEX idx_inv_time ON invocations(started_at DESC);
CREATE INDEX idx_coact_peer ON co_activations(peer_name, observed_at);

L'utilità di una singola invocazione (vedi synapse §3) è u = gap_pre - gap_post; il journal non pre-aggrega per non congelare la formula.

7. Stati di vita

StatoCondizione di entrataEffetto operativo
neonatoFirmato da < 7 giorni e < 20 invocazioni.Selezionato con priorità per retrieval esplorativo; fitness non pesa ancora.
attivoFirma valida, fitness ≥ soglia, silenzio < 90 gg.Candidato normale nell'arena di selezione.
dormiente90 giorni senza invocazioni.Scompare dal retriever normale; ancora raggiungibile via myclaw neuron invoke <name>.
quarantenaFirma mismatch o 3 strike consecutivi (outcome=error nelle ultime N invocazioni) o capability scaduta.Non invocabile. Appare in myclaw neuron status con motivo. Revoca o riabilitazione richiedono approvazione esplicita.
estinto30 giorni di quarantena senza riabilitazione, o rimozione manuale.Directory spostata in .audit/neurons_extinct/YYYY-MM/. File di sola lettura, preservato per forensica.
DECISIONE DA CONFERMARE. Le soglie numeriche (7 gg, 90 gg, 3 strike, 30 gg) sono default conservativi. Saranno tunabili via config. La semantica degli stati è invece stabile.

8. Contratto Python

from typing import Protocol, Literal
from dataclasses import dataclass
from datetime import datetime

@dataclass(frozen=True)
class NeuronManifest:
    name: str
    version: str
    purpose: str
    entry: str                     # "body:run"
    input_schema: dict
    output_schema: dict
    capabilities: list[str]
    quotas: dict
    may_call: list[str]
    tags: list[str]
    created_at: datetime
    parent_trace: str | None

NeuronState = Literal["neonato", "attivo", "dormiente", "quarantena", "estinto"]

@dataclass
class NeuronHandle:
    manifest: NeuronManifest
    path: str                      # workspace/neurons/<nome>/
    state: NeuronState
    signature_ok: bool
    last_invocation: datetime | None

class NeuronLoader(Protocol):
    async def load_all(self) -> list[NeuronHandle]: ...

    async def reload(self, name: str) -> NeuronHandle: ...

    async def verify(self, name: str) -> bool:
        """Ricalcola HMAC, confronta con signature.hmac, aggiorna stato."""
        ...

    async def invoke(
        self,
        name: str,
        ctx: "NeuronContext",
        **kwargs,
    ) -> "ToolResult":
        """Valida input, esegue in sandbox, registra nel journal. Solleva
        SignatureError, QuotaExceededError, UndeclaredSynapseError, ..."""
        ...

    async def set_state(self, name: str, state: NeuronState, reason: str) -> None: ...

# Errori sollevabili dal loader
class SignatureError(Exception): ...
class UndeclaredSynapseError(Exception): ...
class QuotaExceededError(Exception): ...
class NeuronNotFoundError(Exception): ...
class InputSchemaError(Exception): ...
class OutputSchemaError(Exception): ...

9. Implementazione di default

AspettoScelta v1Motivazione
Storage manifestYAML (ruamel.yaml)Commenti preservati; human-editable in caso di review manuale.
Schema validatorjsonschema Draft 2020-12Standard, zero-dep pesante, errori leggibili.
FirmaHMAC-SHA256 (stdlib hmac)Semplice, basta per rilevare tampering locale (vedi §5).
JournalSQLite per-neuroneNessuna contention cross-neurone; scriptabile via CLI sqlite3.
LoaderScan eager all'avvio + watcher inotifyHot-reload senza riavvio del gateway.
Analisi staticaAST whitelist + ruff/banditDettagli in synthesizer §5.

10. Alternative considerate

AlternativaPerché scartata (o rimandata)
Un monolite di neuroni in un unico neurons.dbPerdiamo la possibilità di spostare/archiviare un neurone come unità atomica. L'audit file-by-file diventa ostico.
WASM invece di PythonIsolamento migliore ma tooling più pesante, perdita di accesso ai tool base del runtime. Rimandato a una v2 eventuale.
Firma asimmetrica (Ed25519)Overhead di gestione chiavi pubbliche; non ci serve fin quando i neuroni restano locali. Pronti a migrare quando servirà condividere.
Manifest in JSONMeno leggibile nei diff, nessun commento. YAML vince per use-case human-in-the-loop.
Giornale centralizzato in PostgresDipendenza pesante per uso domestico. SQLite basta per anni.
Nessuna distinzione "neonato"Senza un periodo di prova, la fitness fragile dei nuovi neuroni li farebbe estinguere subito. Corregge un bias di selezione (early-mortality).

11. Test di conformità

InvarianteTest
Manifest obbligatori presentiCaricare un neurone senza name/entry/capabilitiesInputSchemaError all'avvio.
Firma valida = attivoDopo load_all(), handle.signature_ok = True, state ∈ {neonato, attivo, dormiente}.
Tampering = quarantenaModificare 1 byte di body.pyverify() False, state = quarantena, evento neuron.signature.invalid emesso.
Input schema enforcedInvocazione con input mancante → InputSchemaError, nessuna esecuzione, nessun entry in invocations.
Output schema enforcedBody che ritorna oggetto non conforme → OutputSchemaError, outcome error loggato.
Capability enforcementBody che tenta fs-read fuori dalla whitelist → sandbox blocca, outcome error, error_class = "CapabilityDenied".
Sinapsi dichiarataBody che chiama un neurone non in may_callUndeclaredSynapseError.
Quote enforcedBody con time.sleep(wall_time_s+1) → timeout, outcome timeout, quota_exceeded=true.
Journal append-onlyDopo K invocazioni: SELECT COUNT(*) FROM invocations = K, nessun DELETE/UPDATE tollerato.
Hot reloadRifirma dopo bump di versione + touch manifest.yaml → il loader ricarica senza riavvio entro 2s.
Transizione a quarantena dopo 3 strike3 outcome=error consecutivi → state = quarantena automaticamente.
Journal fuori dal payload firmatoBump di invocations non invalida la firma (il journal non è nel payload HMAC).

12. Riferimenti

RiferimentoCosa abbiamo preso
Neuroni+Memoria v1.1 §2, §3, §10Definizione narrativa di "neurone", ciclo di sintesi, anatomia.
Voyager (Wang et al. 2023)Skill library persistente come pattern fondamentale.
Toolformer / GorillaTool-schema validation (input/output) come prima difesa.
CoALA §procedural memoryCollocazione dei neuroni nella tassonomia della memoria.
SynthesizerPipeline che produce corpo + manifest + test (doc dedicato).
SynapseCome il journal di ogni neurone contribuisce al grafo globale.
Policy §4Registro delle capability e vincoli applicabili.
SandboxProfilo bwrap «tight» che esegue body e test.

Continua a leggere

prossimo
synthesizer
Come un neurone nasce: la pipeline di sintesi in 7 stadi.
microprogettazione · 15 min
synapse
Il grafo emergente: fitness, decadimento, potatura.
fondamenti
Neuroni, Sinapsi e Memoria v1.1
Il "perché" dell'estensione darwiniana.
indice
Torna alla landing
Microprogettazione, tutti i doc.

myclaw — neuron microprogettazione v1.0 — 2026-04-22
Primo doc dell'estensione neuroni. Prossimo: synthesizer.html.