action handler: reactive dispatch + native executor + hard-gate uplink + B2-lift (capabilities)#122
Merged
Merged
Conversation
…runs a command
Continues the arago ActionHandler runtime toward a live drop-in. Two bricks:
1. Reactive dispatch + the B1 executor seam (ogar-from-schema/action_ws.rs):
- CapabilityExecutor trait — the seam where a runner plugs in (the only piece
that does real I/O); the RBAC/guard gate (commit_via) is owned by the impl
downstream, keeping the producer crate lance-graph-free.
- handle_submit(msg, def, signature, executor) — the whole handler reaction:
validate id -> ack-or-nack -> bind params -> execute -> sendActionResult.
Rejects invalid id / unknown capability before ack; reports post-ack
bind/exec failure as {"error":...} in the result (documented OGAR convention).
- 5 tests with a mock executor (accept+run, unknown-capability nack, invalid-id
nack, bind-failure, executor-failure).
2. New crate ogar-action-handler — the runtime (B1 native target):
- NativeCommandExecutor runs an ExecuteCommand capability's `command` via a
local POSIX shell, returns output/stderr/exitcode as resultParameters (the
arago SSH-handler shape, minus SSH). Trust model documented (gate is upstream).
- full_dispatch_runs_a_real_command: handle_submit + NativeCommandExecutor run
`echo running` end-to-end -> {"output":"running",...}. OGAR runs a command here.
- SSH/REST/WinRM targets follow the same trait (rs-graph-llm graph-flow-action
for production); this is the reference native impl.
48 tests green across the two crates (43 + 5 + doctest), clippy-clean on new code.
Docs: ARAGO-ACTIONHANDLER-PARITY scorecard (dispatch + native exec SHIPPED;
remaining B2-transport / B2-lift / non-native executors); D-ACTIONHANDLER-PARITY
row. Also scrubbed the internal dev-portal host from the doc (neutral spec refs).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EYvNjD8M8LMNYbRy3gq2FP
|
You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard. |
The hard gate is now wired to the executor in rs-graph-llm's new `graph-flow-action-ogar` seam crate (`GatedOgarHandler` / `run_gated`): `dispatch_via`'s cold floor (`commit_via`: RBAC ∧ state-guard ∧ MUL) lands before the OGAR `CapabilityExecutor` runs. The structural proof is a negative — `take_result()` is `None` whenever the gate refused, so an unauthorized (`Denied`) or MUL-blocked (`Escalated`) action never reaches the executor. OGAR owns the executor; rs-graph-llm owns the gate; the seam crate joins them without OGAR taking a `lance-graph` dep. - ARAGO-ACTIONHANDLER-PARITY: new B1-uplink §3 bullet + scorecard row (SHIPPED), verdict note (gate wired to executor), cross-ref to the seam crate. - DISCOVERY-MAP: new D-ACTIONHANDLER-UPLINK row (G / CODED), cross-ref D-ACTIONHANDLER-PARITY (append-only; existing row untouched). - EPIPHANIES: E-ACTIONHANDLER-UPLINK (FINDING) — the load-bearing proof is the negative test; the coupling lives in the seam, not in graph-flow-action. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01EYvNjD8M8LMNYbRy3gq2FP
Parse a deployed handler's REST registration into the concrete OGAR signatures
the *schema* half cannot supply. The OGIT ontology declares only that a
capability has mandatoryParameters/optionalParameters slots; the concrete
(name, mandatory, default) tuples live in a deployed handler's config. B2-lift
reads them from the live REST view, so the two halves compose: schema lift gives
the contract shape, instance lift gives the deployed values.
Producer stays parser-free (the crate-family split):
- ogar-from-schema/src/registration.rs — typed REST DTOs (RegisteredCapability /
RegisteredParam / ModelFilter, Deserialize behind the existing `serde` feature)
+ the pure lift: lift_registration -> ConcreteCapability (concrete ActionParam[]),
and model_filter_to_guard (arago ModelFilter{Var,Mode,Value} -> StateGuard,
field-for-field). No serde_json, no I/O. 5 tests incl. the lifted signature
driving action_ws::bind_parameters.
- ogar-action-handler — the runtime owns I/O: parse_capabilities does the
serde_json read of a GET /capabilities body. Proven end-to-end by
rest_registration_lifts_binds_and_runs (real JSON -> lift -> bind -> the
NativeCommandExecutor runs the command).
Same producer-defines-types / runtime-does-I/O split the whole crate family
keeps: ogar-from-schema gains a REST front-end without gaining a parser dep.
Remaining for full B2-lift: the GET /applicabilities MapOfApplicabilities JSON
envelope read (the ModelFilter->StateGuard lift is already shipped).
Docs: ARAGO-ACTIONHANDLER-PARITY (B2-lift §3 bullet + two scorecard rows +
verdict + cross-refs), D-ACTIONHANDLER-B2LIFT discovery row, E-ACTIONHANDLER-B2LIFT
epiphany (the parser-free producer / runtime-does-the-read split).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EYvNjD8M8LMNYbRy3gq2FP
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Continues the arago ActionHandler runtime toward a live drop-in. OGAR now runs a real command end-to-end, behind a hard authorization gate, from a real REST registration.
1. Reactive dispatch + the B1 executor seam (
ogar-from-schema/action_ws.rs)CapabilityExecutortrait — the seam where a command runner plugs in (the only piece that does real I/O). The RBAC/guard gate (commit_via) is owned downstream, so the producer crate stayslance-graph-free.handle_submit(msg, def, signature, executor)— validate id → ack-or-nack → bind params → execute →sendActionResult. Rejects invalid id / unknown capability before ack; reports post-ack bind/exec failure as{"error":…}in the result.2. New crate
ogar-action-handler— the runtime (B1 native target)NativeCommandExecutorruns anExecuteCommand'scommandvia a local POSIX shell, returningoutput/stderr/exitcode.full_dispatch_runs_a_real_command:echo runningend-to-end →{"output":"running",…}.3. The hard-gate uplink (documented here; code in rs-graph-llm)
The executor is wired behind the hard gate in rs-graph-llm's new
graph-flow-action-ogarseam (GatedOgarHandler/run_gated):dispatch_via's cold floor (commit_via: RBAC ∧ state-guard ∧ MUL) lands before the OGAR executor'shandle. Structural proof —take_result()isNonewhenever the gate refused, so an unauthorized (Denied) or MUL-blocked (Escalated) action never reaches the executor. OGAR owns the executor; rs-graph-llm owns the gate; the seam joins them without OGAR taking alance-graphdep. Recorded as the B1-uplink scorecard row +D-ACTIONHANDLER-UPLINK+E-ACTIONHANDLER-UPLINK.4. B2-lift — REST registration instance lift, capabilities (
ogar-from-schema/registration.rs+ogar-action-handler)The OGIT ontology declares only that a capability has param slots; the concrete
(name, mandatory, default)tuples live in a deployed handler's config. B2-lift reads them from the live REST view, so the two halves compose.registration.rsdefines the typed REST DTOs (RegisteredCapability/ModelFilter,Deserializebehind theserdefeature) + the pure lift (lift_registration → ConcreteCapabilitywith concreteActionParam[];model_filter_to_guard:ModelFilter{Var,Mode,Value}→StateGuard, field-for-field). Noserde_json, no I/O.ogar-action-handler::parse_capabilitiesdoes theserde_jsonread of aGET /capabilitiesbody.rest_registration_lifts_binds_and_runsproves it end-to-end: real JSON → lift →bind_parameters→NativeCommandExecutorruns the command. Same producer-defines-types / runtime-does-I/O split the whole crate family keeps.Parity state
Contract, lifecycle, protocol binding, reactive dispatch, a working native executor, the hard authorization gate, and the capabilities REST instance lift are all OGAR-native and tested. What remains for a live drop-in of the Python daemon: B2-transport (the WebSocket loop — all message shapes/auth pinned), the B2-lift applicabilities envelope (
GET /applicabilitiesJSON read; theModelFilter→StateGuardlift is done), and the non-native executor targets (SSH/REST). Each is transport/parser/runner glue over existing types — no missing IR.Tests
54 green across the two crates (48
ogar-from-schema+ 6ogar-action-handler+ doctest), clippy-clean on new code. Docs:ARAGO-ACTIONHANDLER-PARITYscorecard +D-ACTIONHANDLER-{PARITY,UPLINK,B2LIFT}ledger rows + epiphanies.Sources:
github.com/arago/ActionHandlers,arago/python-hiro-stonebranch-actionhandler, and the HIRO 7 Action API spec.🤖 Generated with Claude Code