← Indice documentazione Microprogettazione › types

myclaw

types — contratti condivisi, namespace canonico
Microprogettazione v1.0 — 22 aprile 2026
Doc trasversale: sblocca la critica B1 della revisione incrociata.
Introduce PlannedAction (bridge policy↔constitution) e TrustScore (asse di autonomia adattiva).

Pubblico: chi tocca più di un componente. Lettura: 15 min.

Indice

  1. Scopo e confini
  2. Principio: definizione unica, replica proibita
  3. Mappa dei tipi condivisi
  4. Tipi trasversali nuovi
  5. Disciplina di versioning e serializzazione
  6. Cosa NON va qui
  7. Test di conformità (drift detection)
  8. Riferimenti incrociati

1. Scopo e confini

types è l'unico doc che elenca in modo autorevole tutti i tipi dati che attraversano più di un componente di myclaw. Non li ridefinisce: li indicizza. Ogni tipo ha un solo doc-proprietario; qui si traccia chi lo definisce, chi lo consuma, e le regole per evitare drift.

Cos'è

Cosa non è

2. Principio: definizione unica, replica proibita

La revisione incrociata dei 17 doc di fase 0 ha mostrato che gli stessi tipi (ExecutionTrace, ApprovalRequest, ViolationReport, …) erano usati da 3-6 componenti ciascuno, ma definiti — o peggio, redefiniti in piccolo — in doc diversi. Prima di scrivere una riga di codice, fissiamo una regola:

DECISIONE v1 (centralizzazione per riferimento, non per duplicazione): ogni tipo cross-componente ha un solo doc-proprietario, in cui è definito per intero. Gli altri doc lo linkano con file#ancoraN ma non lo ridefiniscono. Il test di drift (§7) fallisce se due doc contengono una class X: con lo stesso nome di tipo cross-componente.

Perché non un modulo Python con tutti i tipi? Perché creerebbe un cerchio: agent_runtime importa da types, types importa da constitution per ViolationReport, e così via. Preferiamo il pattern "owner doc → package di componente": ogni tipo vive nel package del suo componente (src/myclaw/constitution/types.py, src/myclaw/approval/types.py), e src/myclaw/types/__init__.py li re-esporta per scrittura ergonomica (from myclaw.types import ExecutionTrace, ApprovalRequest). Zero dipendenze circolari, unica sorgente di verità.

types.html PlannedAction TrustScore + indice di tutti i cross-type agent_runtime.html ExecutionTrace, TraceStep approval_ux.html ApprovalRequest, Approval constitution.html ViolationReport, LawId tool.html ToolMeta, ToolError memory.html MemoryBundle, Episode channel.html OutboundMessage, Button indice (non ridefinisce) owner unico
types.html è un indice. Ogni tipo ha un solo doc-proprietario. Le frecce tratteggiate rappresentano riferimento, non dipendenza di definizione.

3. Mappa dei tipi condivisi

La tabella sottostante è l'anagrafe canonica dei tipi cross-componente di myclaw. Ogni riga è la coppia <tipo, owner>. Modifiche al tipo richiedono edit del solo owner-doc e allineamento di questa tabella.

Tipo Owner doc § Consumatori principali
ExecutionTrace agent_runtime §5 eval memory policy observability synapse synthesizer rl_offline
TraceStep (union) agent_runtime §5 eval approval_ux observability
ThoughtStep, ActionStep, CriticStep agent_runtime §5 eval observability rl_offline
ApprovalRequest approval_ux §9 gateway channel policy observability
Approval approval_ux §9 gateway agent_runtime observability
BatchGrant approval_ux §9 policy observability
UndoResult approval_ux §9 gateway channel
PolicyDecision policy §7 agent_runtime gateway observability
BudgetStatus policy §7 agent_runtime observability
LawId constitution §10 policy observability
ConstitutionDoc constitution §10 agent_runtime policy
ViolationReport constitution §10 policy agent_runtime observability
ToolMeta tool §2 agent_runtime policy
ToolError tool §5 agent_runtime observability
OutboundMessage, Button channel §2 approval_ux gateway
ChannelCapabilities channel §2 gateway approval_ux
MemoryBundle memory §9 agent_runtime
Episode, SemanticFact memory §9 rl_offline synthesizer
NeuronManifest neuron §10 synthesizer synapse agent_runtime
NeuronHandle, NeuronState neuron §10 synapse agent_runtime rl_offline
PlannedAction nuovo types §4 policy constitution agent_runtime
TrustScore nuovo types §4 policy synapse memory rl_offline
Lettura della tabella. "Owner doc §" è il solo posto in cui è lecito trovare class X: o X = Literal[...]. "Consumatori" indica i doc che importano e/o descrivono l'uso, non che ridefiniscono.

4. Tipi trasversali nuovi

Due tipi non hanno un owner naturale: attraversano più componenti senza appartenere a nessuno in particolare. Vivono qui.

4.1 PlannedAction — bridge policy ↔ constitution

constitution.html §10 dichiara Constitution.check(action: PlannedAction) ma non definisce mai PlannedAction. È una lacuna della revisione incrociata (gap semantico B2). Il tipo serve come richiesta strutturata che la policy sottopone alla constitution prima di decidere il verdetto.

from dataclasses import dataclass
from datetime import datetime
from typing import Literal
from uuid import UUID

EffectKind = Literal[
    "fs_read", "fs_write", "fs_delete",
    "shell_exec", "network_out", "network_in",
    "memory_write", "memory_delete",
    "neuron_spawn", "neuron_invoke", "neuron_promote",
    "outbound_message",          # verso utente / terzi
    "cost_spend",                # LLM / API
    "config_change", "self_modify",
]

@dataclass(frozen=True)
class PlannedAction:
    """
    Descrizione canonica di un'azione che sta per avvenire.
    Passata a Constitution.check() dalla Policy.evaluate().
    Immutable: riflette l'intento registrato, non l'esito.
    """
    kind: EffectKind
    tool_name: str                # tool richiesto (anche "neuron:<name>")
    args_digest: str              # SHA-256 dei parametri (no PII in log)
    args_preview: dict            # versione redatta per UI/prompt
    sender: str                   # "cli:roberto" | "telegram:@roberto" | "cron:..."
    trace_id: UUID
    session_id: str
    autonomy: Literal["read_only", "supervised", "full"]
    targets: list[str]            # path, URL, neuron name — quel che viene toccato
    reversibility: Literal["reversible", "reversible_with_cost", "irreversible"]
    est_cost_eur: float           # 0 se non applicabile
    planned_at: datetime

Perché qui e non in constitution? Perché è costruita dalla policy, letta dalla constitution, persistita dall'observability, e serializzata nella trace. Nessun owner unico naturale. Centralizzandola in types evitiamo dipendenze circolari fra policy e constitution.

Invariante. PlannedAction.args_digest è calcolato sull'intero args, ma args_preview è redatto: niente segreti, token, path assoluti di file utente. La policy e la constitution vedono lo stesso digest; l'audit log e l'approval UX vedono il preview.

Relazione con ApprovalRequestDraft

gateway.html §4.1 introduce ApprovalRequestDraft come output della Policy. I due tipi non sono sinonimi: hanno ruoli distinti e qui li fissiamo canonicamente.

@dataclass(frozen=True)
class ApprovalRequestDraft:
    """
    Restituito dalla Policy quando il verdetto è 'approve_required'.
    È un PlannedAction arricchito di ciò che la Policy ha deciso ma che non
    sa ancora rendere in UI: il Gateway lo completa con canale, sender, timeout
    per produrre una ApprovalRequest finale.
    """
    action: PlannedAction           # riferimento immutabile all'intento
    effect_class: str               # es. "fs_write:workspace"
    summary: str                    # linea leggibile per l'utente
    risk: Literal["low", "medium", "high"]
    policy_reasons: list[str]       # perché richiesta (per explain-mode)

Regola: la Policy NON costruisce ApprovalRequest. Emette solo il Draft. Il Gateway è l'unico che crea la request finale (vedi gateway.html §4.1, decisione "owner unico del flusso"). Questo risolve l'incongruenza §3.3 della review di coerenza.

4.2 TrustScore — asse di autonomia adattiva

La revisione cross-doc ha convergiato su un principio: ridurre il carico di approvazione umana senza perdere sicurezza richiede un'autonomia trust-weighted. Ogni coppia (neurone, effect_class) accumula uno score calcolato da trace storiche; la policy usa lo score per promuovere dinamicamente approve_required → allow sotto soglia di rischio. Nessun training, solo aggregazione.

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

TrustSubject = tuple[str, str]  # (neuron_name | "core", effect_class)

@dataclass
class TrustScore:
    """
    Score aggregato di fiducia per una coppia (attore, classe d'effetto).
    Non è gradient-learned: è media ponderata di segnali oggettivi.
    Tutti i campi in [0.0, 1.0] salvo n_samples.
    """
    subject: TrustSubject
    # Componenti (aggiornate da rl_offline via trace reduce):
    tool_success: float           # % ActionStep con exec_result ok
    human_approval_rate: float    # approve / (approve + deny) storico
    cost_efficiency: float        # 1 − (cost_effettivo / cost_mediano_classe), clip
    constitution_clean: float     # 1 − (n_violation / n_samples)
    undo_rate: float              # % di Approval seguite da undo (invertito nello score)
    # Aggregato:
    score: float                  # 0 = sempre richiedi approvazione, 1 = massima fiducia
    # Metadati:
    n_samples: int                # trace considerate
    last_updated: datetime
    last_decay_at: datetime       # pesi esponenziali Ebbinghaus-like (decay 30g default)
    window: Literal["7d", "30d", "90d", "lifetime"] = "30d"

@dataclass(frozen=True)
class TrustThreshold:
    """Soglia sotto cui la policy promuove approve_required → allow."""
    effect_class: str
    risk_band: Literal["low", "medium", "high"]
    min_score: float              # score minimo richiesto per promozione
    min_samples: int              # n_samples minimi richiesti
    cap_autonomy: Literal[
        "read_only", "supervised", "full",
    ] = "supervised"              # non promuovere sopra questo livello
DECISIONE v1: TrustScore è puramente aggregativo (media ponderata, decay esponenziale). Nessun modello appreso in fase 1. La formula di aggregazione è score = 0.30·tool_success + 0.25·human_approval_rate + 0.20·cost_efficiency + 0.15·constitution_clean + 0.10·(1−undo_rate). Motivo: ogni componente è audit-friendly e direttamente debuggabile. La promozione a modello appreso è deferrata (valutare se/quando in rl_offline.html §esperimenti).
DECISIONE DA CONFERMARE. Le soglie TrustThreshold per ogni effect_class sono un parametro di sicurezza. In v1 sono tutte cap a supervised (cioè il trust abilita l'auto-approve dentro il batch window, ma non rimuove la catena umana se l'autonomy è read_only). La promozione automatica a full resta una scelta dell'utente. Rivalutare dopo 3 mesi di uso.

4.3 Invariante di interazione

I due tipi sono agganciati: la policy costruisce una PlannedAction, legge il TrustScore associato al subject = (tool_name, effect_class), e decide:

# Pseudocodice — la firma reale sta in policy.html §7
def evaluate(tc: dict, trace: ExecutionTrace, sender: str, autonomy: str) -> PolicyDecision:
    action = PlannedAction.from_tool_call(tc, sender, trace, autonomy)

    # 1. Leggi costituzionale, mai scavalcabile
    viol = constitution.check(action)
    if viol and viol.law in ("law.0", "law.1"):
        return PolicyDecision(verdict="deny", reason=viol.reason, ...)

    # 2. Trust-gating per azioni a side-effect
    if action.kind in SIDE_EFFECT_KINDS:
        score = trust_store.get(subject=(action.tool_name, effect_class_of(action)))
        threshold = trust_thresholds.for_effect(effect_class_of(action), action.reversibility)
        if score.n_samples >= threshold.min_samples \
                and score.score >= threshold.min_score \
                and autonomy_le(autonomy, threshold.cap_autonomy):
            return PolicyDecision(verdict="allow", via="trust_promoted", ...)
        return PolicyDecision(verdict="approve_required", ...)

    return PolicyDecision(verdict="allow", ...)

5. Disciplina di versioning e serializzazione

Serializzazione: JSON append-only, schema versionato

Tutti i tipi che finiscono in workspace/.audit/ o in risposta REST seguono un contratto comune.

  1. Campo schema_version obbligatorio in ogni oggetto top-level persistito (es. ExecutionTrace, ApprovalRequest, NeuronManifest). Valore semver corto: "1.0", "1.1", "2.0".
  2. Codifica: JSON UTF-8, datetime come ISO-8601 con Z, UUID come stringa, bytes come base64. Enum Literal sono stringhe.
  3. Append-only: i file di audit sono .jsonl (una riga = un oggetto). Mai riscrivere, mai editare in-place. L'edit di una trace è una nuova trace.
  4. Retro-compatibilità: il loader deve accettare tutti gli schema_version precedenti dello stesso major; campi sconosciuti vengono preservati in unknown_fields: dict per round-trip.
  5. Segreti banditi dal serialized: niente token, niente path con ~/private/, niente chiavi API. La redazione avviene prima della serializzazione (PlannedAction.args_preview è l'esempio canonico).

Versioning degli schema: chi, quando, come

CambioSemverChi autorizzaAnnuncio
Aggiungere un campo opzionale minor Owner-doc autore Changelog nel doc-owner
Aggiungere un valore a un Literal[...] minor Owner-doc autore Changelog nel doc-owner; deprecazione se rimpiazza un altro valore
Rimuovere un campo / rinominare major Gate approvazione umana esplicita Annuncio in types.html changelog + periodo di overlap ≥ 1 release
Cambiare tipo di un campo major Gate approvazione umana esplicita Stesso sopra
Aggiungere un nuovo tipo cross-componente minor Owner-doc + aggiornamento §3 di questo doc Riga nella tabella §3
Perché il gate umano per major? Un cambio major a ExecutionTrace o ApprovalRequest può invalidare scenari eval e trace archiviate. Non succede da solo: è una decisione di progetto, merita frizione intenzionale.

6. Cosa NON va qui

Per evitare di trasformare types.html in un doppione universale, vale questa lista di esclusione:

7. Test di conformità (drift detection)

Il drift fra doc e codice è il peggior nemico di questa struttura. I test sotto sono obbligatori in CI (come gate di eval.html) e contract-enforcing.

InvarianteTest
Ogni tipo in §3 esiste nel codice con lo stesso nome Per ogni riga della tabella §3: importlib.import_module("myclaw.types").__dict__ contiene il simbolo.
Ogni tipo ha un solo doc-proprietario Grep nei 18 doc architettura: per ogni nome di tipo cross-componente, al massimo una class X: o X = Literal[...] esiste. Fallisce se duplicato.
Ogni consumatore dichiarato in §3 importa effettivamente il tipo AST scan del codice: per ogni (tipo, consumatore) in §3, il package src/myclaw/<consumatore>/ ha almeno un from myclaw.types import ...<tipo>.
schema_version presente in ogni oggetto persistito Rileggere 100 trace recenti da workspace/.audit/: json.loads(line)["schema_version"] non solleva.
Round-trip di un oggetto con campi sconosciuti Serializzare una ExecutionTrace v2.0, deserializzarla con loader v1.9: i campi nuovi finiscono in unknown_fields, nessuna eccezione.
Redazione di PlannedAction.args_preview Costruire una PlannedAction con args = {"api_key": "sk-..."}: il preview non deve contenere il valore. args_digest è stabile rispetto all'originale.
Formula TrustScore.score è una combinazione lineare documentata Con componenti tutte a 1.0, score ≈ 1.0 (± 1e-9). Con una a 0.0, score cala di esattamente il peso dichiarato in §4.2.
Cambio major richiede firma Fixture: un doc che sposta da str a int un campo esistente senza toccare schema_version major deve far fallire la CI.
DECISIONE DA CONFERMARE. Il test "Ogni tipo ha un solo doc-proprietario" ha un falso positivo noto: i walking skeleton dentro i doc (es. agent_runtime.html §8) contengono Python eseguibile che ridichiara il tipo per autosufficienza dell'esempio. La convenzione è: walking skeleton va in un blocco <pre> marcato con data-skeleton="true" e il test lo ignora. Da implementare nel linter CI.

8. Riferimenti incrociati

Ogni tipo linkato in §3 punta al doc-proprietario. Questa sezione è il riassunto per lettura rapida.

Owner docTipi canonici
agent_runtime.html §5 ExecutionTrace, TraceStep, ThoughtStep, ActionStep, CriticStep
approval_ux.html §9 ApprovalRequest, Approval, BatchGrant, UndoResult
policy.html §7 PolicyDecision, BudgetStatus
constitution.html §10 LawId, ConstitutionDoc, ViolationReport
tool.html §2, §5 ToolMeta, ToolError
channel.html §2 OutboundMessage, Button, ChannelCapabilities
memory.html §9 MemoryBundle, Episode, SemanticFact
neuron.html §10 NeuronManifest, NeuronHandle, NeuronState
questo doc §4 PlannedAction, EffectKind, TrustScore, TrustThreshold, TrustSubject

Per il razionale architetturale (perché esistono i 4 strati, perché le 4 Leggi, perché il trust-weighted) vedi i doc di Livello 1: Architettura Intro, Prospettive & Giudizio, Neuroni & Memoria.


Continua a leggere

consumatore chiave
policy.html
Consuma PlannedAction, TrustScore, ViolationReport. È il primo utilizzatore di questi tipi.
produttore di TrustScore
rl_offline.html
Come le trace aggregate producono TrustScore. I 3 esperimenti trace-based, synthesizer reward, neuron self-play.
indice microprogettazione
Landing microprogettazione
Tutti i componenti e il loro stato.
home
← Indice documentazione
Tutti i documenti.

myclaw — types microprogettazione v1.0 — 2026-04-22
Doc trasversale. Sblocca B1 della revisione incrociata. Introduce PlannedAction e TrustScore.