gioser
⎇ c6a131ac259b8809d1d65e463a9203e6493decb4 crates/ente-brain/schema/rule.k 167 líneas · 6.0 KB Historia crudo

prueba c6a131ac2 · Sergio ·

1# ============================================================================
2# rule.k — REFERENCE ONLY. NOT LOADED.
3#
4# La gramática autoritativa de Rule vive en Rust:
5#   crates/ente-brain/src/rules.rs
6# El loader (crates/ente-brain/src/loader.rs) sólo acepta JSON / JSONL.
7#
8# Conservado como notas de diseño humano-legibles del shape Rule:
9# Triplet [Sujeto + Evento + Acción(Objeto)]. Cada regla es una sinapsis:
10# cuando ocurre `when`, el motor ejecuta `then` para los Entes que cumplen
11# `scope`. El motor las indexa por discriminante de EventKind para lookup
12# en O(1). Las reglas son inmutables tras carga (Arc<Rule>).
13#
14# Si cambias el shape en Rust, sincroniza este archivo a mano (o
15# reemplázalo por JSON Schema generado vía `schemars`).
16# ============================================================================
17
18schema Rule:
19    """Una sinapsis del fractal. Determinista, sin estado entre disparos."""
20    id: str                              # Ulid 26 chars
21    priority: int = 5                    # 0..255, mayor = se ejecuta primero
22    when: EventPattern
23    then: [Action]
24    scope: Scope = Scope {}              # qué Entes son sujetos válidos
25
26    check:
27        len(id) == 26, "id debe ser Ulid"
28        priority >= 0 and priority <= 255, "priority fuera de rango"
29        len(then) > 0, "regla sin acciones"
30
31
32# ---------- Subject: alcance del sujeto ----------
33
34schema Scope:
35    """Match del sujeto. None en todos los campos = match cualquier Ente."""
36    subject_id?: str                     # Ulid exacto
37    subject_label?: str                  # label exacto
38    subject_has_cap?: Capability         # Ente que declara esta capacidad
39
40    check:
41        subject_id is None or len(subject_id) == 26, "subject_id no es Ulid"
42
43
44# ---------- Event: qué dispara la regla ----------
45
46# EventPattern es tagged union recursivo.
47#
48# Atómicos:
49#   Single   — match un evento por kind
50#   Sequence — N eventos consecutivos dentro de within_ms
51#
52# Compuestos (recursivos):
53#   Either   — OR sobre sub-patterns
54#   All      — AND sobre sub-patterns (mismo event/history)
55schema EventPattern:
56    type: "Single" | "Sequence" | "Either" | "All"
57    kind?: EventKind                     # Single
58    kinds?: [EventKind]                  # Sequence
59    within_ms?: int = 0
60    patterns?: [EventPattern]            # Either / All (recursivo)
61
62    check:
63        type != "Single" or kind is not None, "Single requiere kind"
64        type != "Sequence" or (kinds is not None and len(kinds) > 0), \
65            "Sequence requiere kinds no vacío"
66        type != "Either" or (patterns is not None and len(patterns) > 0), \
67            "Either requiere patterns no vacío"
68        type != "All" or (patterns is not None and len(patterns) > 0), \
69            "All requiere patterns no vacío"
70        within_ms is None or within_ms >= 0, "within_ms negativo"
71
72
73# EventKind con tag interno + payload opcional según tag.
74schema EventKind:
75    tag: "EnteSpawned" | "EnteDied" | "BusAnnounce" | "BusInvoke" | "BusInvokeOf" | "DeviceAdded" | "DeviceRemoved" | "Custom"
76    cap?: Capability                     # para BusInvokeOf
77    custom?: str                         # para Custom
78
79    check:
80        tag != "BusInvokeOf" or cap is not None, "BusInvokeOf requiere cap"
81        tag != "Custom" or custom is not None, "Custom requiere custom string"
82
83
84# ---------- Action: qué hacer ----------
85
86schema Action:
87    """Una acción ejecutable por el motor. Tagged union con kind."""
88    kind: "Log" | "Notify" | "Spawn" | "Invoke" | "Inhibit"
89    # Log
90    level?: "trace" | "debug" | "info" | "warn" | "error"
91    message?: str
92    # Notify
93    target_id?: str                      # Ulid
94    # Spawn
95    card_blob?: str                      # base64-encoded EntityCard JSON
96    # Invoke
97    target_cap?: Capability
98    blob_b64?: str
99    # Inhibit
100    reason?: str
101
102    check:
103        kind != "Log" or message is not None, "Log requiere message"
104        kind != "Notify" or (target_id is not None and message is not None), \
105            "Notify requiere target_id + message"
106        kind != "Spawn" or card_blob is not None, "Spawn requiere card_blob"
107        kind != "Invoke" or target_cap is not None, "Invoke requiere target_cap"
108        kind != "Inhibit" or reason is not None, "Inhibit requiere reason"
109
110
111# ---------- Capability: re-export desde card.k para evitar inclusión circular ----------
112
113# En uso real: `import ..ente_card.schema.card` y referencia Capability.
114# Aquí declaramos una versión alineada para auto-contención del esquema.
115schema Capability:
116    kind: "FilesystemRoot" | "KernelNetlink" | "Endpoint" | "LegacyLogind" | "Device" | "Spawn" | "Journal"
117    netlink_family?: "Uevent" | "Route" | "Generic" | "Audit"
118    endpoint_interface?: str
119    endpoint_version?: int
120    device_class?: "Block" | "Tty" | "Input" | "Drm" | "Net" | "Hidraw"
121
122
123# ============================================================================
124# Ejemplo de regla cristalizada (auto-generada por el observador)
125# ============================================================================
126
127example_rule = Rule {
128    id = "01KQQ100000000000000000000"
129    priority = 5
130    when = EventPattern {
131        type = "Single"
132        kind = EventKind {tag = "EnteSpawned"}
133    }
134    scope = Scope {
135        subject_label = "demo-echo"
136    }
137    then = [
138        Action {
139            kind = "Log"
140            level = "info"
141            message = "demo-echo encarnado, observando para crystallization"
142        }
143    ]
144}
145
146# Ejemplo de regla compuesta: cuando un Ente se anuncia y luego es invocado
147# en menos de 500ms, log estructurado para auditoría.
148example_sequence = Rule {
149    id = "01KQQ200000000000000000000"
150    priority = 7
151    when = EventPattern {
152        type = "Sequence"
153        kinds = [
154            EventKind {tag = "BusAnnounce"}
155            EventKind {tag = "BusInvoke"}
156        ]
157        within_ms = 500
158    }
159    scope = Scope {}
160    then = [
161        Action {
162            kind = "Log"
163            level = "info"
164            message = "patrón Announce→Invoke detectado <500ms"
165        }
166    ]
167}