You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
docs: record B2-transport — the transport-agnostic action daemon (ws edge)
The live action daemon shipped in rs-graph-llm's graph-flow-action-ogar::daemon:
a transport-agnostic core (Daemon::react/serve) + a Transport trait + the
action-ws WebSocket edge (WsTransport), proven by a mock-server roundtrip. HIRO
distributes actions over both a WebSocket and an internal Kafka bus, so the
dispatch is written once and the wire is a thin Transport shell — Kafka is the
reserved second edge. Connection identity is an Auth type shaped after OGIT
NTO/Auth/Configuration (auth_store 0x0B01): the principal that connects is the
actor the gate authorizes.
- ARAGO-ACTIONHANDLER-PARITY: B2-transport §3 bullet + two scorecard rows
(daemon+ws SHIPPED, kafka edge remaining), verdict, cross-ref to daemon.rs.
- DISCOVERY-MAP: D-ACTIONHANDLER-TRANSPORT (G core+ws / H kafka).
- EPIPHANIES: E-ACTIONHANDLER-TRANSPORT — multi-wire ⇒ transport-agnostic core;
OGIT Auth unifies connect-identity with gate-actor.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EYvNjD8M8LMNYbRy3gq2FP
Copy file name to clipboardExpand all lines: .claude/board/EPIPHANIES.md
+40Lines changed: 40 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -7,6 +7,46 @@
7
7
8
8
---
9
9
10
+
## 2026-06-24 — E-ACTIONHANDLER-TRANSPORT — the daemon is transport-agnostic because HIRO is multi-wire; and the OGIT Auth type unifies "who connects" with "who the gate authorizes"
11
+
12
+
**Status:** FINDING (`[G]` for the core + WebSocket edge; `[H]` for the Kafka edge).
13
+
14
+
Two design facts surfaced building B2-transport (the live action daemon, in
15
+
rs-graph-llm `graph-flow-action-ogar::daemon`):
16
+
17
+
1.**HIRO distributes actions over more than one wire** — a handler-facing
18
+
WebSocket (`action-ws`) AND an internal Kafka bus that legacy handlers consume
19
+
directly (operator note, 2026-06-24). The wire differs; the dispatch doesn't.
20
+
So the daemon is factored as: `Daemon::react` (the transport-agnostic core —
21
+
one inbound `action-ws` frame → outbound frames, running the gate + executor,
22
+
pure/no-I/O) + a `Transport` trait (`recv`/`send`, the swappable edge) +
23
+
`Daemon::serve` (the loop, generic over `Transport`). The WebSocket edge
24
+
(`WsTransport`) and a future Kafka edge (`rdkafka`) share `serve` verbatim —
25
+
the gated dispatch is written once, the wire is a thin shell. This is the
26
+
action-arm analogue of the codec stack's "one algebra, many carriers": one
27
+
dispatch, many transports.
28
+
29
+
2.**The OGIT Auth type unifies the two identities that must be the same.** A
30
+
handler's connection presents a credential; the gate authorizes an actor.
31
+
These MUST be the same principal — and OGIT's `NTO/Auth/Configuration` (the
32
+
`auth_store` class, OGAR `0x0B01`) already unifies them: it is keyed by
33
+
`accountId` and maps `sub` → actor (`0x0104`), org/tenant → scope. So the
34
+
daemon's `Auth` type is shaped after it: one value carries the `token` the
35
+
transport presents (the `token-$TOKEN` subprotocol) AND the `account` the gate
36
+
authorizes as (`accountId` → actor). `Daemon::new` takes `&Auth` and derives
37
+
the gate actor from `auth.account`; `WsTransport::connect` takes `&Auth` and
38
+
presents `auth.token`. The identity that connects IS the identity the RBAC
39
+
grant is checked against — structurally, not by convention. (A future
40
+
producer-side `auth_from_ogit(entity)` lift would populate `Auth` from a real
41
+
`NTO/Auth/Configuration` node, the same way `assemble_action_handler` lifts the
42
+
handler contract.)
43
+
44
+
Proven by `ws_roundtrip_against_a_mock_server` (engine `submitAction` → ack → real
45
+
command → result over a live socket) + 10 pure-core tests. Scorecard: B2-transport
## 2026-06-24 — E-ACTIONHANDLER-B2LIFT — the producer stays parser-free even when lifting a JSON REST response: it defines the `Deserialize` DTOs + the pure lift, the runtime does the `from_str`
11
51
12
52
**Status:** FINDING (`[G]` for capabilities; `[H]` for the applicabilities envelope).
|**Live transport — Kafka edge (B2-transport)**| ⛔ `[H]`|`rdkafka` over the same `Transport` trait (action topic → result topic); core ready, needs the topic/record shape pinned |
Copy file name to clipboardExpand all lines: docs/DISCOVERY-MAP.md
+1Lines changed: 1 addition & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -213,6 +213,7 @@ two halves of a cell. ADR‑026 names the cascade that ties them.
213
213
| D‑ACTIONHANDLER‑PARITY | arago HIRO ActionHandler ⟷ OGAR: `assemble_action_handler` walks the OGIT `provides` graph (`ActionHandler→ActionApplicability→ActionCapability`) into `ActionHandlerSpec`/`CapabilitySlot`/`ApplicabilitySlot`/`ActionParam`. Config+ontology+`action-ws` protocol all map to OGAR types: arago `ModelFilter{Var,Mode,Value}`→`StateGuard`; `Capability.Name`→`predicate`; `resultParameters`→output sig; `action-ws` `submitAction→ack→sendActionResult` ⟷ `ActionInvocation` `Pending→Committed` (`commit_via` is the gate). **B2 protocol core SHIPPED + spec-faithful** (`action_ws`: all 6 `action-ws.yaml` message types — submitAction/sendActionResult/acknowledged/negativeAcknowledged/configChanged/error — + `submit_to_invocation`/`bind_parameters`/`invocation_to_result` (result=JSON string ≤1 MiB per spec) + connection consts (`ACTION_WS_PATH`, `auth_subprotocol`, `validate_id`); socket-free, `full_action_ws_roundtrip` proven; harvested from the HIRO 7.0 dev-portal specs §2a). **Reactive dispatch + B1 native executor SHIPPED**: `action_ws::handle_submit` (validate→ack/nack→bind→execute→result) over the `CapabilityExecutor` trait (the B1 seam); `ogar-action-handler::NativeCommandExecutor` runs `ExecuteCommand` for real (`full_dispatch_runs_a_real_command` — OGAR runs a command end-to-end). Remaining for a live drop-in: B2-transport (WS loop), B2-lift (REST registration parse), SSH/REST executor targets; gated on `PROBE‑OGAR‑ACTIONHANDLER‑RUN` | G (contract+protocol+native exec) / H (live socket) | CODED | `ogar-from-schema/src/{do_arm,action_ws}.rs`, `ogar-action-handler/`, `docs/ARAGO-ACTIONHANDLER-PARITY.md` | D‑HIRO‑DO, D‑MARS‑CLASSID |
214
214
| D‑ACTIONHANDLER‑UPLINK | The hard gate wired to the OGAR executor (cross‑repo seam): rs‑graph‑llm `graph-flow-action-ogar::GatedOgarHandler` wraps an OGAR `CapabilityExecutor` as a `graph-flow-action::ActionHandler`, so `dispatch_via`'s cold floor (`commit_via`: def‑match → RBAC `ClassRbac` → `StateGuard` → MUL) lands **before** the executor's `handle`. Structural proof the contract lands first: `take_result()`/`run_gated` returns `None` whenever the gate refused — unauthorized actor → `Denied` (executor never runs), MUL `Block` → `Escalated` (executor never runs); only the authorized path reaches `NativeCommandExecutor` and runs the real command (3 tests). Dependency hygiene held: `graph-flow-action` stays contract‑only (`I‑ACTIONHANDLER‑IS‑KGV‑NOT‑CHOKEPOINT`); `ogar-from-schema` carries no `lance-graph` dep — the two sides meet only at this crate's API (one `lance-graph-contract`). rs‑graph‑llm pinned to toolchain 1.95.0 to match the AdaWorldAPI stack | G | CODED | `rs-graph-llm/graph-flow-action-ogar/src/lib.rs` | D‑ACTIONHANDLER‑PARITY |
215
215
| D‑ACTIONHANDLER‑B2LIFT | REST registration **instance lift** (B2-lift) — turns a deployed handler's `GET /capabilities` JSON (`MapOfCapabilities`) into the concrete signatures the *schema* half can't supply: `registration::{RegisteredCapability,ModelFilter}` (typed DTOs, `Deserialize` behind the `serde` feature) + the pure lift `lift_registration → ConcreteCapability` (concrete `ActionParam[]` with `(name,mandatory,default)`) and `model_filter_to_guard` (arago `ModelFilter{Var,Mode,Value}`→`KausalSpec::StateGuard`, field‑for‑field). Producer stays parser‑free; the runtime `ogar-action-handler::parse_capabilities` does the `serde_json` read (producer‑defines‑types / runtime‑does‑I/O split). Proven end‑to‑end: `rest_registration_lifts_binds_and_runs` (real JSON → lift → `bind_parameters` → `NativeCommandExecutor` runs the command). Remaining: the `GET /applicabilities` `MapOfApplicabilities` envelope read (the `ModelFilter→StateGuard` lift is done). **CORRECTION (canon‑pass 2026‑06‑24): applicabilities envelope now SHIPPED** — `registration::{RegisteredApplicability,lift_applicabilities}` + `ogar-action-handler::parse_applicabilities` lift a real `GET /applicabilities` JSON body into per‑handler `StateGuard` sets (`rest_applicabilities_lift_to_per_handler_guards`); inner filter‑list field name alias‑flexible (`modelFilters`/`model`/`filters`) pending a live response. Supersedes the "Remaining" note | G | CODED | `ogar-from-schema/src/registration.rs`, `ogar-action-handler/src/lib.rs` | D‑ACTIONHANDLER‑PARITY |
216
+
| D‑ACTIONHANDLER‑TRANSPORT | The live action daemon (B2-transport), **transport-agnostic** by construction: rs‑graph‑llm `graph-flow-action-ogar::daemon::Daemon::react` turns one inbound `action-ws` JSON frame into the outbound frames it warrants (ack + `sendActionResult`, or nack), running the hard gate (`run_gated`) + executor in between — pure, no I/O. A `Transport` trait (`recv`/`send`) is the swappable edge; `Daemon::serve` is the loop; both the WebSocket and a future Kafka edge share it verbatim (HIRO distributes actions over BOTH wires — the wire differs, the dispatch doesn't). The `WsTransport` action-ws edge (`feature = "ws"`, tokio-tungstenite) presents the `token-$TOKEN` subprotocol and is proven by `ws_roundtrip_against_a_mock_server` (engine submitAction → ack → real command → result over a socket). Connection identity is an `Auth` type shaped after OGIT `NTO/Auth/Configuration` (`auth_store` 0x0B01): the principal the transport authenticates as (`accountId`) IS the actor the gate authorizes. Remaining: the Kafka edge (`rdkafka` over the same trait — core ready, needs topic/record shape) and SSH/REST executors | G (core + ws edge) / H (kafka edge) | CODED | `rs-graph-llm/graph-flow-action-ogar/src/daemon.rs` | D‑ACTIONHANDLER‑UPLINK, D‑ACTIONHANDLER‑B2LIFT |
216
217
| D‑OSM |`ogar-from-osm-pbf` — Node/Way/Relation; quadkey NiblePath from resolved geometry | H | IDEA | (queued) | D‑VOCAB, `[per rt]` D‑OSM‑3 |
217
218
| D‑PATTERN |`ogar-pattern` — recognition library + confidence (FMA‑D/FIBO/SKR/PROV‑O) | H | IDEA | (queued) | D‑TTL |
218
219
| D‑ACTION |`ogar-actionable` — lifecycle → `ActionDef`/`KausalSpec`| H | IDEA | (queued) | D‑PATTERN |
0 commit comments