Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions .claude/board/EPIPHANIES.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,50 @@

---

## 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

**Status:** FINDING (`[G]`, 19 tests).

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:

1. **Transport-agnostic** — `Transport` trait (WebSocket today, Kafka reserved).
2. **Class-agnostic** — `ClassResolver` resolves the action class from the **target
node's classid** *at dispatch time*, not wired at build time. The production
`OgarResolver` is backed by the canonical `actions_for(&[ClassActions], classid)`
DO manifest — the exact `classid → ClassActions` surface OGAR already generates.
3. **Executor-agnostic** — the executor is chosen from what the class resolves to
(`RunnerKind` → `ExecutorRegistry`); `RegistryExecutor` adapts it so the gate
runs first and the concrete runner is picked **post-commit**.

`ResolvingDaemon` holds NO wired classes and NO wired executor. The same
`submitAction` (`ExecuteCommand`) dispatches to native (`mars_machine`) or REST
(`mars_resource`) purely by what the target's classid resolves to — **zero daemon
change**. A new capability / class / runner is a registry entry, never code:
exactly *"scale = the next cascade level, never field-widening."*

**Why this is the same canon, not a new one.** The CLAUDE.md P0 says a
renderer/router *"can lay out, group, route, and skeleton-render nodes from keys
alone, before (or without ever) fetching a value."* The action daemon is precisely
such a router: the `classid` in the GUID simultaneously selects the transport edge
it arrived on, the class's `ActionDef`, the state-guard, the executor (`RunnerKind`),
and the RBAC concept (`lo16`) — all before any value decode. The hi-u16 chooses the
render skin per app, the lo-u16 the shared concept (consumer doctrine). The action
arm didn't need a new abstraction; it needed to *be* the key-is-the-key-of-key-value
store applied to behavior. The hard gate (`commit_via`) is untouched — late binding
selects WHICH action, never whether it's authorized.

**Fence:** the resolver needs a populated `ClassActions` manifest to resolve
against — which is exactly what B2-lift produces (`parse_capabilities` →
signatures; the deployed `GET /capabilities` IS the registry content). Empty
registry ⇒ resolves nothing (zero-fallback, never a panic); B2-lifted registration
⇒ resolves everything. Cross-ref: `D-ACTIONHANDLER-RESOLVER`,
`D-ACTIONHANDLER-TRANSPORT` (transport axis), `D-ACTIONHANDLER-B2LIFT` (the
registry content).

---

## 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"

**Status:** FINDING (`[G]` for the core + WebSocket edge; `[H]` for the Kafka edge).
Expand Down
22 changes: 19 additions & 3 deletions docs/ARAGO-ACTIONHANDLER-PARITY.md
Original file line number Diff line number Diff line change
Expand Up @@ -269,6 +269,7 @@ transport over them.
| **Executor — WinRM (B1)** | ⛔ `[H]` | a further `CapabilityExecutor` impl (Windows remote exec) |
| **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` |
| **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 |
| **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 |
| **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) |
| **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 |

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

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

---

## 5. The probe that promotes B1/B2 from `[H]` to `[G]`
Expand Down Expand Up @@ -331,9 +345,11 @@ is replaceable; the parity claim is certified, not argued.
- `rs-graph-llm/graph-flow-action-ogar` — the **uplink**: OGAR's
`CapabilityExecutor` behind the hard gate (`GatedOgarHandler` / `run_gated`);
`commit_via` lands before any execution.
- `rs-graph-llm/graph-flow-action-ogar/src/daemon.rs` — **B2-transport**: the
transport-agnostic `Daemon` (`react`/`serve`) + the `Transport` trait +
`WsTransport` (action-ws WebSocket edge) + the OGIT-`Auth`-derived identity.
- `rs-graph-llm/graph-flow-action-ogar/src/daemon.rs` — **B2-transport** + **the
grail**: the transport-agnostic `Daemon` (`react`/`serve`) + the `Transport`
trait + `WsTransport` (action-ws WebSocket edge) + the OGIT-`Auth`-derived
identity; **plus** `ResolvingDaemon` + `ClassResolver` / `ExecutorRegistry` /
`OgarResolver` (class chosen late from the classid via `actions_for`).
- `rs-graph-llm/graph-flow-action-ogar/src/rest.rs` — the **REST executor**
(`RestExecutor`, `feature = "rest"`): the arago HTTP-callout target, gated.
- `crates/ogar-action-handler/src/lib.rs` — the **native** (`NativeCommandExecutor`)
Expand Down
1 change: 1 addition & 0 deletions docs/DISCOVERY-MAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@ two halves of a cell. ADR‑026 names the cascade that ties them.
| 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 |
| 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 |
| 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 |
| 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 |
| D‑OSM | `ogar-from-osm-pbf` — Node/Way/Relation; quadkey NiblePath from resolved geometry | H | IDEA | (queued) | D‑VOCAB, `[per rt]` D‑OSM‑3 |
| D‑PATTERN | `ogar-pattern` — recognition library + confidence (FMA‑D/FIBO/SKR/PROV‑O) | H | IDEA | (queued) | D‑TTL |
| D‑ACTION | `ogar-actionable` — lifecycle → `ActionDef`/`KausalSpec` | H | IDEA | (queued) | D‑PATTERN |
Expand Down
Loading