|
| 1 | +# HIRO → OGAR DO-arm lift — lossless actionable semantics from the Automation domain |
| 2 | + |
| 3 | +> **Status:** FINDING (mapping + principle, grounded) + CONJECTURE (the producer). |
| 4 | +> **Preflight:** authored under `docs/SURREAL-AST-TRAP-PREFLIGHT.md` — behavior |
| 5 | +> flows **producer → OGAR `Class`+`ActionDef` → adapter**, never producer → DDL. |
| 6 | +> **Reads:** OGIT `NTO/Automation/entities/*` (KnowledgeItem · ActionHandler · |
| 7 | +> ActionCapability · ActionApplicability · Trigger · Intent · AutomationIssue · |
| 8 | +> History · Variable), `docs/OGAR-AST-CONTRACT.md` (the DO-arm types). |
| 9 | +
|
| 10 | +The MARS import lifted the **structural** arm (A→R→S→M `Class`es). The |
| 11 | +**Automation** domain — HIRO's actuator vocabulary — is the **behavioral** arm |
| 12 | +the structural import left on the table. This doc maps it into OGAR's DO arm and |
| 13 | +pins the one rule that keeps the lift **lossless**. |
| 14 | + |
| 15 | +--- |
| 16 | + |
| 17 | +## 1. The lossless-DO principle (the governing rule) |
| 18 | + |
| 19 | +A DO compiler is **lossy** when it tries to express behavior in **one flat |
| 20 | +target** (a DDL `DEFINE EVENT … WHEN … THEN …`, a single schema row). Behavior |
| 21 | +has **three irreducible slices**, and any single format holds at most one or two: |
| 22 | + |
| 23 | +| Slice | Home (lossless) | Carries | |
| 24 | +|---|---|---| |
| 25 | +| **Identity + structure** — *what* is acted on | `Class` / `ClassView` (THINK arm) | the **address** | |
| 26 | +| **Contract + lifecycle** — *how* it's invoked | `ActionDef` IR + the trait (`StateMachine` / `UnifiedStep`) | the typed **control** (predicate · object_class · `KausalSpec` · `results_in` · `on_enter` · `guard_failure_policy` · `state_timeout_millis` · idempotency) | |
| 27 | +| **Executable body** — the *actual* command | the **adapter** binding | the **behavior**, verbatim | |
| 28 | + |
| 29 | +> **The rule: DO is lossless iff the `ActionDef` *points to* the body |
| 30 | +> (content-addressed) instead of *compressing it into* DDL.** This is |
| 31 | +> `I-VSA-IDENTITIES` applied to the DO arm — an identity that points to content, |
| 32 | +> never a bundle of the content's register. The `classid` is the join key across |
| 33 | +> all three slices; nothing is projected away because each slice has a home that |
| 34 | +> holds it exactly. |
| 35 | +
|
| 36 | +It is **not** "interfaces *vs* adapters *vs* classes" — it is **all three**. The |
| 37 | +lossiness is precisely what happens when one of them is forced to carry all three |
| 38 | +(the DDL flatten: keeps "when X then Y", drops the lifecycle / guard-failure |
| 39 | +modality / SLA / the `ActionDef`(static) ÷ `ActionInvocation`(dynamic) split — |
| 40 | +the "negative-beauty hijack" `SURREAL-AST-AS-ADAPTER.md §0` rejects). |
| 41 | + |
| 42 | +--- |
| 43 | + |
| 44 | +## 2. Export shape — what NOT-lossy looks like on the wire |
| 45 | + |
| 46 | +Not a DDL. Export **three artifacts joined by `classid`**: |
| 47 | + |
| 48 | +1. **ActionDef manifest** — a typed SoA record (the contract). **Wire-truth** per |
| 49 | + the Firewall (ADR-022/023): the IR *is* the wire, no re-serialization. |
| 50 | +2. **Payload table** — the body as an **opaque, content-addressed blob** |
| 51 | + (`hash → bytes`), resolved by the adapter at invocation. The IR holds the |
| 52 | + *pointer* (classid/hash), never the bytes. |
| 53 | +3. **ClassView** — identity (the join key). |
| 54 | + |
| 55 | +The adapter is *where behavior executes* (Core-First: it **assumes** the Core via |
| 56 | +`classid`, never carries its own state); the interface (`StateMachine` / |
| 57 | +`UnifiedStep`) is *how it's invoked*; the class is *what it's addressed by*. |
| 58 | +Round-trip is lossless: the body is preserved verbatim, the contract as typed IR. |
| 59 | + |
| 60 | +--- |
| 61 | + |
| 62 | +## 3. HIRO is already this shape (the external proof) — `[G]` |
| 63 | + |
| 64 | +arago's Automation graph is a production, lossless DO encoding. Read directly: |
| 65 | + |
| 66 | +- **`KnowledgeItem`** carries the contract as relations + lifecycle attrs |
| 67 | + (`uses Variable`, `contains Trigger`, `worksOn AutomationIssue`, |
| 68 | + `relates MARSNodeTemplate`, `solves Task`, `generates Timeseries`, |
| 69 | + `deployToEngine` / `deployStatus`) **and the body rides as |
| 70 | + `knowledgeItemFormalRepresentation` — an opaque blob the schema references but |
| 71 | + never parses.** That is the identity-points-to-body split, verbatim. |
| 72 | +- **`AutomationIssue generates History`** is literally OGAR-AST-CONTRACT's |
| 73 | + "state history IS the version log." |
| 74 | +- **`ActionHandler` connects `Configuration` + `ActionCapability` + |
| 75 | + `ActionApplicability`** — and `Configuration` ⊨ `auth_store` (`0x0B01`, the |
| 76 | + family we minted). The **DO arm and the auth/RBAC arm meet at `ActionHandler`.** |
| 77 | + |
| 78 | +We do not invent the shape; we adopt the one arago validated at scale. |
| 79 | + |
| 80 | +--- |
| 81 | + |
| 82 | +## 4. Mapping — OGIT Automation → OGAR DO arm |
| 83 | + |
| 84 | +| OGIT `Automation` entity | OGAR DO-arm target | Evidence (the actionable fields) | |
| 85 | +|---|---|---| |
| 86 | +| `KnowledgeItem` | **`ActionDef`** | `knowledgeItemFormalRepresentation` = opaque body (→ payload table); `uses Variable` = params/defaults; `relates MARSNodeTemplate` = `object_class`; `solves Task` / `generates Timeseries` = `results_in` | |
| 87 | +| `ActionCapability` | `ActionDef` params + **adapter binding** | `mandatoryParameters`; "remote execution via ssh" = the executor the adapter wires | |
| 88 | +| `ActionApplicability` | **`KausalSpec::StateGuard`** | `environmentFilter`; "on `ogit/_id`" = the guard predicate (WHEN the action applies) | |
| 89 | +| `ActionHandler` | the **adapter** (+ membrane) | connects `Configuration` (= `auth_store` `0x0B01`) + `Capability` + `Applicability` | |
| 90 | +| `Trigger` | **`KausalSpec::LifecycleTrigger`** | "Trigger of a KI having a set of Subitems" | |
| 91 | +| `Intent` | **`ActionDef.results_in`** / desiredState | "The Intent of a KI processing chain" | |
| 92 | +| `AutomationIssue` | **`ActionInvocation`** | situational data (Key/Value tuples); `worksOn MARSNode` = `object_instance`; `generates History` | |
| 93 | +| `History` | `ActionInvocation` **state log** | = the Lance version log ("state history IS the version log") | |
| 94 | +| `Variable` | `ActionDef` params / `defaults` | `uses Variable` | |
| 95 | +| `MARSNode` / `MARSNodeTemplate` | `object_class` / `object_instance` | the THINK arm — **already imported** structurally | |
| 96 | +| `Configuration` | `auth_store` (`0x0B01`) | already minted; the membrane the handler authorizes through | |
| 97 | + |
| 98 | +--- |
| 99 | + |
| 100 | +## 5. Worked example — `KnowledgeItem` → `ActionDef` |
| 101 | + |
| 102 | +```text |
| 103 | +ogit.Automation:KnowledgeItem "restart-stuck-service" |
| 104 | + knowledgeItemFormalRepresentation = <XML body / script> ─┐ opaque |
| 105 | + uses ogit.Automation:Variable (serviceName, host) │ payload |
| 106 | + contains ogit.Automation:Trigger (onIssueType=ServiceDown) │ (content- |
| 107 | + relates ogit.Automation:MARSNodeTemplate (Software/Service) │ addressed, |
| 108 | + worksOn ogit.Automation:AutomationIssue │ never |
| 109 | + generates ogit:Timeseries (restart latency) ─┘ parsed) |
| 110 | + │ lift (shape only — body stays a blob) |
| 111 | + ▼ |
| 112 | +ActionDef { |
| 113 | + predicate: "restart-stuck-service", |
| 114 | + object_class: Identity(Software/Service), // ← relates MARSNodeTemplate |
| 115 | + kausal: LifecycleTrigger { on: ServiceDown }, // ← contains Trigger |
| 116 | + defaults: [serviceName, host], // ← uses Variable |
| 117 | + results_in: Some(StateTransition::to(Restarted)), // ← Intent / generates |
| 118 | + guard_failure_policy: Postponable, // ← env: transient → retry |
| 119 | + payload_ref: blake3(formalRepresentation), // ← POINTER, not the bytes |
| 120 | +} |
| 121 | +// the <XML body> lives in the payload table @ blake3(...), executed by the adapter. |
| 122 | +``` |
| 123 | + |
| 124 | +Lossless check: every lifecycle/guard/param field is a typed `ActionDef` field; |
| 125 | +the executable body is preserved verbatim behind a content hash. A DDL emit of |
| 126 | +this would keep "on ServiceDown, run X" and **lose** `guard_failure_policy`, |
| 127 | +`results_in` typing, and the `ActionDef`÷`ActionInvocation` split. |
| 128 | + |
| 129 | +--- |
| 130 | + |
| 131 | +## 6. The producer (proposed) — `[H]`, gated on a probe |
| 132 | + |
| 133 | +Parallel to `ogar-from-schema` (structural arm), an Automation-domain DO lift |
| 134 | +(extend `ogar-from-schema` with a `do_arm` module, reusing its TTL parser): |
| 135 | + |
| 136 | +```text |
| 137 | +Automation/entities/*.ttl → ActionDef{ predicate, object_class, kausal, |
| 138 | + defaults, results_in, payload_ref } |
| 139 | + + payload table (formalRepresentation blobs) |
| 140 | +``` |
| 141 | + |
| 142 | +- **`payload_ref` only** — the lift NEVER inlines or reparses |
| 143 | + `knowledgeItemFormalRepresentation`; it hashes it into the payload table. |
| 144 | +- **CONJECTURE until `PROBE-OGAR-DO-ARM-LIFT` is green:** an `ActionDef` → |
| 145 | + adapter → execute → result must reproduce the KI's recorded behavior |
| 146 | + bit-for-bit on a fixed corpus (the same falsification discipline as |
| 147 | + `PROBE-OGAR-RBAC-AUTHORIZE`). Until then the mapping in §4 is a FINDING about |
| 148 | + *shape*, not a certified executable equivalence. |
| 149 | + |
| 150 | +--- |
| 151 | + |
| 152 | +## 7. One-line rule |
| 153 | + |
| 154 | +> **DO is lossless iff the `ActionDef` points to the body, never flattens it into |
| 155 | +> DDL.** Identity → content; three slices (class · interface · adapter) joined by |
| 156 | +> `classid`; export the typed manifest + the opaque payload, never the DDL. |
0 commit comments