Skip to content

Commit 82412bd

Browse files
committed
docs: record the grail — class-late-bound action dispatch (ResolvingDaemon)
The action arm reached its holy grail, and it turned out to be a restatement of OGAR's most basic canon ("the key prerenders the node; classid → ClassView"), not a new mechanism. Three axes of agnosticism, all keyed by the GUID: transport (Transport trait), class (ClassResolver resolves from the target's classid at dispatch time, production OgarResolver backed by the canonical actions_for manifest), executor (chosen from the resolved RunnerKind via ExecutorRegistry, runner picked post-commit). ResolvingDaemon holds no wired classes and no wired executor; the same ExecuteCommand dispatches to native (mars_machine) or REST (mars_resource) by what the classid resolves to, zero daemon change, gate still rules. A new capability/class/runner is a registry entry, never code. - ARAGO-ACTIONHANDLER-PARITY: new scorecard row (class-late-bound dispatch SHIPPED), verdict paragraph (the grail), cross-ref to daemon.rs. - DISCOVERY-MAP: D-ACTIONHANDLER-RESOLVER (G / CODED). - EPIPHANIES: E-ACTIONHANDLER-RESOLVER — the action daemon IS a renderer over the classid keyspace; the grail is the key-is-key-of-KV store applied to behavior. Code lives in rs-graph-llm PR #20 (graph-flow-action-ogar::daemon). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EYvNjD8M8LMNYbRy3gq2FP
1 parent ea19e0e commit 82412bd

3 files changed

Lines changed: 64 additions & 3 deletions

File tree

.claude/board/EPIPHANIES.md

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,50 @@
77
88
---
99

10+
## 2026-06-24 — E-ACTIONHANDLER-RESOLVER — the action daemon IS a renderer over the classid keyspace: transport, class, executor, guard, RBAC all fall out of the GUID, late
11+
12+
**Status:** FINDING (`[G]`, 19 tests).
13+
14+
The action arm reached its holy grail — and it turned out to be a restatement of
15+
OGAR's most basic canon (*"the key prerenders the node; classid → ClassView"*),
16+
not a new mechanism. Three axes of agnosticism, all keyed by the GUID:
17+
18+
1. **Transport-agnostic**`Transport` trait (WebSocket today, Kafka reserved).
19+
2. **Class-agnostic**`ClassResolver` resolves the action class from the **target
20+
node's classid** *at dispatch time*, not wired at build time. The production
21+
`OgarResolver` is backed by the canonical `actions_for(&[ClassActions], classid)`
22+
DO manifest — the exact `classid → ClassActions` surface OGAR already generates.
23+
3. **Executor-agnostic** — the executor is chosen from what the class resolves to
24+
(`RunnerKind``ExecutorRegistry`); `RegistryExecutor` adapts it so the gate
25+
runs first and the concrete runner is picked **post-commit**.
26+
27+
`ResolvingDaemon` holds NO wired classes and NO wired executor. The same
28+
`submitAction` (`ExecuteCommand`) dispatches to native (`mars_machine`) or REST
29+
(`mars_resource`) purely by what the target's classid resolves to — **zero daemon
30+
change**. A new capability / class / runner is a registry entry, never code:
31+
exactly *"scale = the next cascade level, never field-widening."*
32+
33+
**Why this is the same canon, not a new one.** The CLAUDE.md P0 says a
34+
renderer/router *"can lay out, group, route, and skeleton-render nodes from keys
35+
alone, before (or without ever) fetching a value."* The action daemon is precisely
36+
such a router: the `classid` in the GUID simultaneously selects the transport edge
37+
it arrived on, the class's `ActionDef`, the state-guard, the executor (`RunnerKind`),
38+
and the RBAC concept (`lo16`) — all before any value decode. The hi-u16 chooses the
39+
render skin per app, the lo-u16 the shared concept (consumer doctrine). The action
40+
arm didn't need a new abstraction; it needed to *be* the key-is-the-key-of-key-value
41+
store applied to behavior. The hard gate (`commit_via`) is untouched — late binding
42+
selects WHICH action, never whether it's authorized.
43+
44+
**Fence:** the resolver needs a populated `ClassActions` manifest to resolve
45+
against — which is exactly what B2-lift produces (`parse_capabilities`
46+
signatures; the deployed `GET /capabilities` IS the registry content). Empty
47+
registry ⇒ resolves nothing (zero-fallback, never a panic); B2-lifted registration
48+
⇒ resolves everything. Cross-ref: `D-ACTIONHANDLER-RESOLVER`,
49+
`D-ACTIONHANDLER-TRANSPORT` (transport axis), `D-ACTIONHANDLER-B2LIFT` (the
50+
registry content).
51+
52+
---
53+
1054
## 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"
1155

1256
**Status:** FINDING (`[G]` for the core + WebSocket edge; `[H]` for the Kafka edge).

docs/ARAGO-ACTIONHANDLER-PARITY.md

Lines changed: 19 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -269,6 +269,7 @@ transport over them.
269269
| **Executor — WinRM (B1)** |`[H]` | a further `CapabilityExecutor` impl (Windows remote exec) |
270270
| **Live transport — daemon core + WebSocket (B2-transport)** |`[G]` SHIPPED | rs-graph-llm `graph-flow-action-ogar::daemon`: transport-agnostic `Daemon::react`/`serve` + `Transport` trait + `WsTransport` (action-ws), gate-driving; mock-server roundtrip. `Auth` ← OGIT `NTO/Auth/Configuration` |
271271
| **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 |
272+
| **Class-late-bound dispatch (the grail)** |`[G]` SHIPPED | rs-graph-llm `graph-flow-action-ogar::daemon::ResolvingDaemon` — class resolved from the target's **classid** per action (`ClassResolver`), executor from the resolved `RunnerKind` (`ExecutorRegistry`). `OgarResolver` is the production resolver over the canonical `actions_for(&[ClassActions], classid)` manifest. Proven: one `ExecuteCommand`, `mars_machine` → native / `mars_resource` → REST, zero daemon change; gate still rules |
272273
| **Instance config lift — capabilities (B2-lift)** |`[G]` SHIPPED | `registration::lift_registration` + `ogar-action-handler::parse_capabilities`: real `GET /capabilities` JSON → `ConcreteCapability` (`ActionParam[]`); `rest_registration_lifts_binds_and_runs` (JSON → lift → bind → run) |
273274
| **Instance config lift — applicabilities (B2-lift)** |`[G]` SHIPPED | `registration::lift_applicabilities` + `ogar-action-handler::parse_applicabilities`: real `GET /applicabilities` JSON → per-handler `StateGuard` sets; `rest_applicabilities_lift_to_per_handler_guards`. Residual: inner filter-list field name is alias-flexible pending a live response |
274275

@@ -299,6 +300,19 @@ runs commands locally / over SSH / as HTTP callouts, and speaks `action-ws` over
299300
live socket; a Kafka consumer away from being arago's Python daemon, on a HIRO
300301
deployment that distributes over Kafka.
301302

303+
**The grail — class chosen late from the classid.** Beyond the static daemon, the
304+
`ResolvingDaemon` (`graph-flow-action-ogar::daemon`) holds **no** wired classes and
305+
**no** wired executor: it resolves the action class from the target node's
306+
**classid** per action (`ClassResolver`), and the executor from what that class
307+
resolves to (`RunnerKind``ExecutorRegistry`). The production resolver
308+
(`OgarResolver`) is backed by the canonical `actions_for(&[ClassActions], classid)`
309+
DO manifest — OGAR's *"the key prerenders the node; classid → ClassView"* applied
310+
to the action arm. One `ExecuteCommand` dispatches to native (`mars_machine`) or
311+
REST (`mars_resource`) purely by what the classid resolves to, with zero daemon
312+
change — and every action still passes the same hard gate. A new capability /
313+
class / runner is a registry entry, never code (*"scale = the next cascade level,
314+
never field-widening"*).
315+
302316
---
303317

304318
## 5. The probe that promotes B1/B2 from `[H]` to `[G]`
@@ -331,9 +345,11 @@ is replaceable; the parity claim is certified, not argued.
331345
- `rs-graph-llm/graph-flow-action-ogar` — the **uplink**: OGAR's
332346
`CapabilityExecutor` behind the hard gate (`GatedOgarHandler` / `run_gated`);
333347
`commit_via` lands before any execution.
334-
- `rs-graph-llm/graph-flow-action-ogar/src/daemon.rs`**B2-transport**: the
335-
transport-agnostic `Daemon` (`react`/`serve`) + the `Transport` trait +
336-
`WsTransport` (action-ws WebSocket edge) + the OGIT-`Auth`-derived identity.
348+
- `rs-graph-llm/graph-flow-action-ogar/src/daemon.rs`**B2-transport** + **the
349+
grail**: the transport-agnostic `Daemon` (`react`/`serve`) + the `Transport`
350+
trait + `WsTransport` (action-ws WebSocket edge) + the OGIT-`Auth`-derived
351+
identity; **plus** `ResolvingDaemon` + `ClassResolver` / `ExecutorRegistry` /
352+
`OgarResolver` (class chosen late from the classid via `actions_for`).
337353
- `rs-graph-llm/graph-flow-action-ogar/src/rest.rs` — the **REST executor**
338354
(`RestExecutor`, `feature = "rest"`): the arago HTTP-callout target, gated.
339355
- `crates/ogar-action-handler/src/lib.rs` — the **native** (`NativeCommandExecutor`)

docs/DISCOVERY-MAP.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,7 @@ two halves of a cell. ADR‑026 names the cascade that ties them.
216216
| 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 |
217217
| D‑ACTIONHANDLER‑REST | REST executor target (B1) — the arago HTTP-callout handler shape: rs‑graph‑llm `graph-flow-action-ogar::rest::RestExecutor` (`feature = "rest"`, pure-Rust `ureq`, sync — fits the sync `CapabilityExecutor` trait) POSTs the bound params as a JSON body to a configured endpoint, returns the response `status`+`body` as `resultParameters`. Any completed HTTP response (incl. 4xx/5xx) is `resultParameters`; only a transport failure is an executor `Err` (mirrors arago reporting the callee's response). `Clone` (ureq Agent is Arc-backed) ⟹ composes into `Daemon`/`run_gated` as a gated route. Proven: `posts_params_and_returns_status_and_body` (mock HTTP) + `rest_executor_runs_only_behind_the_gate` (authorized → REST call fires; unauthorized → `Denied`, endpoint never hit). Completes the executor family with native; SSH/WinRM remain | G | CODED | `rs-graph-llm/graph-flow-action-ogar/src/rest.rs` | D‑ACTIONHANDLER‑UPLINK |
218218
| D‑ACTIONHANDLER‑SSH | SSH executor target (B1) — arago's canonical `ExecuteCommand`-over-SSH: `ogar-action-handler::SshExecutor` shells out to the system `ssh` binary (dep-free, like `NativeCommandExecutor` shells to `sh`), non-interactive by construction (`BatchMode=yes`), same `output`/`stderr`/`exitcode` resultParameters shape — the native executor made remote. `build_args` (pure argv construction with `-i`/`-p` + `--` command terminator) and the pre-spawn guards (missing-command / unknown-capability) are tested; end-to-end remote exec needs a live sshd (absent in CI). The two Command-based dep-free executors (native + SSH) live in OGAR; library-based network executors (REST) in rs-graph-llm | G (code) / H (live exec) | CODED | `ogar-action-handler/src/lib.rs` | D‑ACTIONHANDLER‑REST |
219+
| D‑ACTIONHANDLER‑RESOLVER | The grail — **class-late-bound** action dispatch: rs‑graph‑llm `graph-flow-action-ogar::daemon::ResolvingDaemon` holds NO wired classes and NO wired executor. `ClassResolver` resolves the action class from the **target's classid** per action; `ExecutorRegistry` picks the executor from the resolved `RunnerKind` (`RegistryExecutor` adapts it so `run_gated` drives it — runner chosen POST-commit). The three axes (transport / class / executor) all key off the GUID. The production resolver `OgarResolver` is backed by the canonical `actions_for(&[ClassActions], classid)` DO manifest (no new dep — lance-graph-contract already wired); `target_classid` reads a 32-hex node GUID prefix OR a concept name (`canonical_concept_id`); classid→RunnerKind + capability→signature (B2-lift) complete the resolve. THIS is "the key prerenders the node; classid → ClassView" applied to the action arm — a new capability/class/runner is a registry entry, never a daemon change. Proven: one `ExecuteCommand`, `mars_machine`→native runs the real command / `mars_resource`→REST, zero daemon change, gate still rules; `ogar_resolver_drives_the_grail_via_actions_for` + GUID-prefix test. Shared `dispatch_action` core (static `Daemon` funnels through it too, behavior-preserving) | G | CODED | `rs-graph-llm/graph-flow-action-ogar/src/daemon.rs` | D‑ACTIONHANDLER‑TRANSPORT, D‑ACTIONHANDLER‑B2LIFT |
219220
| D‑OSM | `ogar-from-osm-pbf` — Node/Way/Relation; quadkey NiblePath from resolved geometry | H | IDEA | (queued) | D‑VOCAB, `[per rt]` D‑OSM‑3 |
220221
| D‑PATTERN | `ogar-pattern` — recognition library + confidence (FMA‑D/FIBO/SKR/PROV‑O) | H | IDEA | (queued) | D‑TTL |
221222
| D‑ACTION | `ogar-actionable` — lifecycle → `ActionDef`/`KausalSpec` | H | IDEA | (queued) | D‑PATTERN |

0 commit comments

Comments
 (0)