|
| 1 | +# OGAR der Türsteher mit Köpfchen — ActionHandler as classes, RBAC hardcoded, Rung as Flughöhe |
| 2 | + |
| 3 | +> **Status: DOCTRINE v0 (2026-06-23).** Pairs with `HIRO-IN-CLASSES.md` |
| 4 | +> (what bardioc gains), `HIRO-DO-ARM-LIFT.md` (the DO-arm mapping + |
| 5 | +> lossless-DO rule), `CLASSID-RBAC-KEYSTONE-SPEC.md` (the four-axis |
| 6 | +> ReBAC keystone), and ada-docs `architecture/COLD_PATH_MUL_ACTIONHANDLER.md` |
| 7 | +> (the cold-path / MUL / kgV-ActionHandler treatment from the consumer side). |
| 8 | +> Most of this doc is `[G]` — it is grounded in shipped code, cited inline. |
| 9 | +
|
| 10 | +The operator's pin, verbatim: *"wie wäre es wenn wir den actionhandler mit |
| 11 | +Klassen im OGAR mit einbauen — dann hat Hiro/Bardioc das Gefühl, daß sie da |
| 12 | +gleich andocken können, und die Flughöhe darüber machen wir mit den Rung-Levels |
| 13 | +(1-9); die Flughöhe sind dann im Hot-Path und RBAC ist hardcoded in OGAR — das |
| 14 | +macht sich bei Compliance-Leuten, die von Semantik keine Ahnung haben, gut. OGAR |
| 15 | +der Türsteher mit Köpfchen."* And the containment pin: *"ob der da oben AGI hat, |
| 16 | +braucht sie nicht zu beunruhigen — OGAR kriegt sie alle."* |
| 17 | + |
| 18 | +This doc carves that into the canon in three moves: **(1)** the ActionHandler |
| 19 | +lives as OGAR classes; **(2)** RBAC is a compile-time `const` on those classes, |
| 20 | +not a runtime policy; **(3)** the cognition above is bounded by the Rung |
| 21 | +Flughöhe in the hot path, and bounded by *structure* — not by trust. |
| 22 | + |
| 23 | +--- |
| 24 | + |
| 25 | +## 1. ActionHandler IS OGAR classes — so HIRO/Bardioc dock native `[G]` |
| 26 | + |
| 27 | +The DO arm of a `Class` is a `const` table of `ActionDef`s — the action-axis |
| 28 | +sibling of the THINK-arm `ClassView`. This is now shipped as the |
| 29 | +**`OgarActionProvider`** (`lance-graph/crates/lance-graph-ogar/src/actions.rs`): |
| 30 | + |
| 31 | +``` |
| 32 | +classid ──► ClassActions { classid, actions: &'static [ActionDef] } |
| 33 | + actions_for(classid) — own surface (zero-fallback &[]) |
| 34 | + effective_actions(classid) — own ∪ parent, overrides applied |
| 35 | +``` |
| 36 | + |
| 37 | +The worked seed is the auth family (keystone §7's `0x0B` core domain): |
| 38 | + |
| 39 | +| class | classid | DO surface | |
| 40 | +|---|---|---| |
| 41 | +| `auth_store` | `0x0B01` | `issue_token` · `revoke_token` · `rotate_secret` | |
| 42 | +| `auth_zitadel` | `0x0B02` | *(is-a `auth_store`)* + `sync_org_roles`, `issue_token` overridden | |
| 43 | + |
| 44 | +`auth_zitadel` **is-a** `auth_store`, so its *effective* surface is the base's |
| 45 | +plus its net-new, with `issue_token` overridden onto the Zitadel |
| 46 | +org-role-aware path — the exact `classid → ClassView` inheritance the field-set |
| 47 | +already uses (`contract::action::effective_actions`). HIRO's `gen_statem` |
| 48 | +lifecycles and Bardioc's handlers dock here by becoming `ActionDef` rows on the |
| 49 | +same `Class` they already are (`HIRO-IN-CLASSES.md` §1, §3.1) — they do not |
| 50 | +wrap OGAR, they *are* OGAR. **No adapter carries its own action table** (the |
| 51 | +core-first invariant): the harvest IS the manifest. |
| 52 | + |
| 53 | +This is the "andocken" the operator wants: a HIRO engineer adds a behaviour by |
| 54 | +adding a `const ActionDef`, not by writing a new dispatcher. |
| 55 | + |
| 56 | +--- |
| 57 | + |
| 58 | +## 2. RBAC is hardcoded — `required_role` is a `const` on the class `[G]` |
| 59 | + |
| 60 | +Every mutating `ActionDef` carries its `required_role` as a **compile-time |
| 61 | +literal**, not a runtime policy row: |
| 62 | + |
| 63 | +```rust |
| 64 | +// lance-graph-ogar/src/actions.rs (the auth_store DO surface) |
| 65 | +ActionDef { predicate: "rotate_secret", object_class: 0x0B01, |
| 66 | + required_role: Some("auth_admin"), |
| 67 | + guard: Some(StateGuard { field: "status", value: "active" }), |
| 68 | + exec: ExecTarget::Native, overrides: None } |
| 69 | +``` |
| 70 | + |
| 71 | +Why a `const`, and why this is the Türsteher: |
| 72 | + |
| 73 | +- **A compliance reviewer reads the grant surface off the source.** No database, |
| 74 | + no admin console, no "effective permissions" query — the class's authorization |
| 75 | + is `grep required_role`. For Compliance-Leute who do not read semantics, this |
| 76 | + is the legible artifact: *who may rotate the secret? `auth_admin`, full stop, |
| 77 | + pinned in the class.* |
| 78 | +- **A roleless mutating action is structurally visible.** `required_role: None` |
| 79 | + is an explicit, audited declaration ("unguarded"), never an accidental open |
| 80 | + door that a missing policy row would create. The test asserts the invariant |
| 81 | + directly: *every mutating auth action has `Some(role)`* |
| 82 | + (`auth_store_surface_has_hardcoded_rbac_on_every_mutating_action`). |
| 83 | +- **An override cannot silently widen access.** `auth_zitadel::issue_token` |
| 84 | + restates `required_role: Some("auth_user")` — the override is `[G]`-tested to |
| 85 | + keep the grant, not elevate it (`zitadel_inherits_auth_store_and_overrides_issue_token`). |
| 86 | + |
| 87 | +The hardcoded role is the **§3 axis-1 verb gate** of the keystone, frozen at the |
| 88 | +class. The richer runtime grant *map* (which actor holds `auth_admin`) is still |
| 89 | +data — resolved by an `auth_store` OIDC profile from token claims (keystone §7, |
| 90 | +I-K7). The class fixes *what role is required*; the token fixes *who has it*. The |
| 91 | +typed `granted` value-tenant that carries the runtime half is now shipped too: |
| 92 | +`contract::rbac::{OpMask, ClassGrant, grants_permit}` (keystone §6) — the |
| 93 | +first-class replacement for `project_role.permissions: text`. |
| 94 | + |
| 95 | +--- |
| 96 | + |
| 97 | +## 3. The cold path the class enforces — { RBAC · Libet · Rubikon@MUL } `[G]` |
| 98 | + |
| 99 | +An `ActionDef`'s `required_role` and `guard` are the **def half** of the |
| 100 | +cold-path gate. The whole hard lifecycle is already in |
| 101 | +`contract::action::ActionInvocation::commit`, which adjudicates in order: |
| 102 | + |
| 103 | +``` |
| 104 | +def-match → RBAC (required_role vs ActorContext) |
| 105 | + → state guard (Libet do/don't — fire only if field==value) |
| 106 | + → MUL impact (the Rubicon crossing: Flow→Committed, Hold→Pending, Block→Cancelled) |
| 107 | +``` |
| 108 | + |
| 109 | +The kgV executor that runs a *committed* action is `graph-flow-action`'s |
| 110 | +`ActionHandler` (rs-graph-llm); the outer planning-cycle envelope is |
| 111 | +`graph-flow-kanban`'s `KanbanPlanEnvelope` over the 4-phase Rubicon board |
| 112 | +(`Planning → CognitiveWork → Evaluation → {Commit|Plan|Prune}`). OGAR supplies |
| 113 | +the *authorized DO surface* both sit over. Three layers, one rule |
| 114 | +(ada-docs `COLD_PATH_MUL_ACTIONHANDLER.md`): |
| 115 | + |
| 116 | +| layer | what | where | |
| 117 | +|---|---|---| |
| 118 | +| **cold path** (hard floor) | RBAC · Libet do/don't · Rubikon last phase · SLA | OGAR class `const` + `commit` | |
| 119 | +| **MUL** (one soft window) | `GateDecision::{Flow,Hold,Block}` | planner / cognition, *before* the Libet veto | |
| 120 | +| **hot path** (execution) | `ActionHandler::handle` | the executor, reached only on `Committed` | |
| 121 | + |
| 122 | +The kgV invariant (`I-ACTIONHANDLER-IS-KGV-NOT-CHOKEPOINT`): thinking stays |
| 123 | +**wide and upstream**; it never crawls under the executor waist. The class fixes |
| 124 | +the floor; cognition decides above it; neither can lower the floor. |
| 125 | + |
| 126 | +--- |
| 127 | + |
| 128 | +## 4. Rung 1-9 is the Flughöhe — cognition altitude in the hot path `[G]` |
| 129 | + |
| 130 | +The operator's "Flughöhe darüber" is the **Rung** (`contract::cognitive_shader::RungLevel`, |
| 131 | +`Surface`=0 … `Transcendent`=9) — *how deep the thinking goes*. It is a hot-path |
| 132 | +quantity (it rides `ShaderDispatch.rung`), distinct from the **execution cost |
| 133 | +ladder** `ElevationLevel` (`Point`=0 … `Async`=5) — *how much machinery the |
| 134 | +search spends*. The two are now calibrated: |
| 135 | + |
| 136 | +```rust |
| 137 | +// lance-graph-planner/src/elevation/mod.rs |
| 138 | +ElevationLevel::from_rung(rung) -> ElevationLevel // monotone non-decreasing |
| 139 | +// Surface/Shallow→Point · Contextual/Analogical→Scan · Abstract/Structural→Cascade |
| 140 | +// Counterfactual→Batch · Meta→IvfBatch · Recursive/Transcendent→Async |
| 141 | +``` |
| 142 | + |
| 143 | +Deeper cognition implies a higher cost **floor of ambition** — but only a floor. |
| 144 | +The **Csíkszentmihályi Flow channel**, already in code as |
| 145 | +`mul::FlowState{Flow,Anxiety,Boredom,Transition}` + `flow_state_from(challenge, |
| 146 | +skill)` driving `elevation::homeostasis`, tunes the actual spend around that |
| 147 | +floor: `Boredom` (skill ≫ challenge) raises the patience budget → go *deeper* |
| 148 | +than the rung floor; `Anxiety` (challenge ≫ skill) cuts it → cap *below*. So the |
| 149 | +Rung sets where the search starts; the flow-modulated budget decides where it |
| 150 | +ends. The Flughöhe is real, measured, and self-regulating — not a knob. |
| 151 | + |
| 152 | +This is why the Rung lives *above* the cold path: it modulates *how hard the |
| 153 | +system thinks before it decides*, never *whether the class lets it act*. A |
| 154 | +Transcendent-rung deliberation and a Surface-rung reflex hit the **same** `commit` |
| 155 | +gate with the **same** `required_role`. Altitude does not buy authority. |
| 156 | + |
| 157 | +--- |
| 158 | + |
| 159 | +## 5. Containment by structure — "OGAR kriegt sie alle" `[H]` |
| 160 | + |
| 161 | +The operator: *"ob der da oben AGI hat, braucht sie nicht zu beunruhigen."* The |
| 162 | +claim is that OGAR contains whatever sits above it **without needing to trust |
| 163 | +it** — and the mechanism is structural, not behavioural: |
| 164 | + |
| 165 | +- **Capability-bounding, not alignment-bounding.** The cognition above can be |
| 166 | + arbitrarily capable; it still acts only through `ActionDef`s that exist, with |
| 167 | + `required_role`s it cannot edit, through a `commit` gate it cannot bypass. The |
| 168 | + bound is on *what doors exist*, not on *how clever the agent is*. An AGI that |
| 169 | + wants to `rotate_secret` still needs `auth_admin` — the class does not care how |
| 170 | + it reasoned its way to the request. |
| 171 | +- **The DO surface is closed at the Core.** There is no "register a new action |
| 172 | + at runtime" path; the manifest is `const`, harvested, reviewed. Cognition |
| 173 | + cannot mint itself a capability. (Contrast: a tool-use agent that can author |
| 174 | + arbitrary shell commands has an *open* DO surface — the failure mode this |
| 175 | + closes.) |
| 176 | +- **Move/ownership semantics make the gate unforgeable.** `commit` consumes the |
| 177 | + invocation's state transition; there is no aliased path to `Committed` that |
| 178 | + skips the RBAC/Libet/MUL checks (the Firewall: no serialization in the hot |
| 179 | + path, ADR-022/023). The compiler is the bouncer's muscle. |
| 180 | + |
| 181 | +So the Türsteher has *Köpfchen* precisely because the intelligence is in the |
| 182 | +**doorway geometry**, not in vetting each guest's intentions. You do not have to |
| 183 | +solve alignment of the thing above to bound what it can do — you only have to fix |
| 184 | +the doors. That is a structural containment claim (`[H]`: argued from the shipped |
| 185 | +`const`-surface + `commit` gate; the falsifier is any path that reaches an |
| 186 | +external mutation without transiting `commit`). |
| 187 | + |
| 188 | +--- |
| 189 | + |
| 190 | +## 6. Cross-references |
| 191 | + |
| 192 | +- `HIRO-IN-CLASSES.md` — the four efficiency wins bardioc gains (dispatch, |
| 193 | + validation, dep-graph, similarity) by living as OGAR classes. |
| 194 | +- `HIRO-DO-ARM-LIFT.md` — HIRO Automation → DO arm mapping; the lossless-DO rule |
| 195 | + (`ActionDef` points-to-body, never flattens into DDL). |
| 196 | +- `CLASSID-RBAC-KEYSTONE-SPEC.md` — §3 four axes, §5 `authorize`, §6 typed |
| 197 | + `granted`, §7 the `0x0B` AuthStore family, §10 `PROBE-OGAR-RBAC-AUTHORIZE`. |
| 198 | +- `SURREAL-AST-AS-ADAPTER.md` — why the behavioural arm cannot live in DDL (the |
| 199 | + reason RBAC is a class `const`, not a `DEFINE EVENT … WHEN … THEN`). |
| 200 | +- ada-docs `architecture/COLD_PATH_MUL_ACTIONHANDLER.md` — the cold-path / MUL / |
| 201 | + kgV-ActionHandler / containment treatment (consumer-side companion). |
| 202 | +- Shipped code: `lance-graph/crates/lance-graph-ogar/src/actions.rs` |
| 203 | + (`OgarActionProvider`), `…/lance-graph-contract/src/{action,rbac,kanban}.rs`, |
| 204 | + `…/lance-graph-planner/src/elevation/mod.rs` (`from_rung`), |
| 205 | + `rs-graph-llm/graph-flow-action` (kgV `ActionHandler`), |
| 206 | + `rs-graph-llm/graph-flow-kanban` (`KanbanPlanEnvelope`). |
0 commit comments