From 121eee3ccd82cb41ffa10469d2497b85b83c2d28 Mon Sep 17 00:00:00 2001 From: despiegk Date: Mon, 25 Aug 2025 07:07:59 +0200 Subject: [PATCH] ... --- docs/JOBS_QUICKSTART.md | 209 ------------------------ docs/PROJECT_OVERVIEW.md | 216 ------------------------- docs/REDIS_QUEUES_GUIDE.md | 199 ----------------------- docs/REDIS_QUEUES_NAMING_PROPOSAL.md | 231 --------------------------- docs/RPC_IMPLEMENTATION.md | 124 -------------- 5 files changed, 979 deletions(-) delete mode 100644 docs/JOBS_QUICKSTART.md delete mode 100644 docs/PROJECT_OVERVIEW.md delete mode 100644 docs/REDIS_QUEUES_GUIDE.md delete mode 100644 docs/REDIS_QUEUES_NAMING_PROPOSAL.md delete mode 100644 docs/RPC_IMPLEMENTATION.md diff --git a/docs/JOBS_QUICKSTART.md b/docs/JOBS_QUICKSTART.md deleted file mode 100644 index 48b3c0a..0000000 --- a/docs/JOBS_QUICKSTART.md +++ /dev/null @@ -1,209 +0,0 @@ -# Jobs Quickstart: Create and Send a Simple Job to the Supervisor - -This guide shows how a new (simple) job looks, how to construct it, and how to submit it to the Supervisor. It covers: -- The minimal fields a job needs -- Picking an actor via script type -- Submitting a job using the Rust API -- Submitting a job via the OpenRPC server over Unix IPC (and WS) - -Key references: -- [rust.ScriptType](core/job/src/lib.rs:16) determines the target actor queue -- [rust.Job](core/job/src/lib.rs:87) is the canonical job payload stored in Redis -- [rust.JobBuilder::new()](core/job/src/builder.rs:47), [rust.JobBuilder::caller_id()](core/job/src/builder.rs:79), [rust.JobBuilder::context_id()](core/job/src/builder.rs:74), [rust.JobBuilder::script_type()](core/job/src/builder.rs:69), [rust.JobBuilder::script()](core/job/src/builder.rs:84), [rust.JobBuilder::timeout()](core/job/src/builder.rs:94), [rust.JobBuilder::build()](core/job/src/builder.rs:158) -- [rust.SupervisorBuilder::new()](core/supervisor/src/lib.rs:124), [rust.SupervisorBuilder::build()](core/supervisor/src/lib.rs:267) -- [rust.Supervisor::create_job()](core/supervisor/src/lib.rs:642), [rust.Supervisor::start_job()](core/supervisor/src/lib.rs:658), [rust.Supervisor::run_job_and_await_result()](core/supervisor/src/lib.rs:672), [rust.Supervisor::get_job_output()](core/supervisor/src/lib.rs:740) -- Redis key namespace: [rust.NAMESPACE_PREFIX](core/job/src/lib.rs:13) - - -## 1) What is a “simple job”? - -A simple job is the minimal unit of work that an actor can execute. At minimum, you must provide: -- caller_id: String (identifier of the requester; often a public key) -- context_id: String (the “circle” or execution context) -- script: String (the code to run; Rhai for OSIS/SAL; HeroScript for V/Python) -- script_type: ScriptType (OSIS | SAL | V | Python) -- timeout: Duration (optional; default used if not set) - -The job’s script_type selects the actor and thus the queue. See [rust.ScriptType::actor_queue_suffix()](core/job/src/lib.rs:29) for mapping. - - -## 2) Choosing the actor by ScriptType - -- OSIS: Rhai script, sequential non-blocking -- SAL: Rhai script, blocking async, concurrent -- V: HeroScript via V engine -- Python: HeroScript via Python engine - -Pick the script_type that matches your script/runtime requirements. See design summary in [core/docs/architecture.md](core/docs/architecture.md). - - -## 3) Build and submit a job using the Rust API - -This is the most direct, strongly-typed integration. You will: -1) Build a Supervisor -2) Construct a Job (using the “core” job builder for explicit caller_id/context_id) -3) Submit it with either: - - create_job + start_job (two-step) - - run_job_and_await_result (one-shot request-reply) - -Note: We deliberately use the core job builder (hero_job) so we can set caller_id explicitly via [rust.JobBuilder::caller_id()](core/job/src/builder.rs:79). - -Example Rhai script (returns 42): -```rhai -40 + 2 -``` - -Rust example (two-step create + start + poll output): -```rust -use hero_supervisor::{SupervisorBuilder, ScriptType}; -use hero_job::JobBuilder as CoreJobBuilder; - -#[tokio::main] -async fn main() -> anyhow::Result<()> { - // 1) Build a Supervisor - let supervisor = SupervisorBuilder::new() - .redis_url("redis://127.0.0.1/") - .build() - .await?; - - // 2) Build a Job (using core job builder to set caller_id, context_id) - let job = CoreJobBuilder::new() - .caller_id("02abc...caller") // required - .context_id("02def...context") // required - .script_type(ScriptType::SAL) // select the SAL actor - .script("40 + 2") // simple Rhai script - .timeout(std::time::Duration::from_secs(10)) - .build()?; // returns hero_job::Job - - let job_id = job.id.clone(); - - // 3a) Store the job in Redis - supervisor.create_job(&job).await?; - - // 3b) Start the job (pushes ID to the actor’s Redis queue) - supervisor.start_job(&job_id).await?; - - // 3c) Fetch output when finished (or poll status via get_job_status) - if let Some(output) = supervisor.get_job_output(&job_id).await? { - println!("Job {} output: {}", job_id, output); - } else { - println!("Job {} has no output yet", job_id); - } - - Ok(()) -} -``` - -Rust example (one-shot request-reply): -```rust -use hero_supervisor::{SupervisorBuilder, ScriptType}; -use hero_job::JobBuilder as CoreJobBuilder; - -#[tokio::main] -async fn main() -> anyhow::Result<()> { - let supervisor = SupervisorBuilder::new() - .redis_url("redis://127.0.0.1/") - .build() - .await?; - - let job = CoreJobBuilder::new() - .caller_id("02abc...caller") - .context_id("02def...context") - .script_type(ScriptType::SAL) - .script("40 + 2") - .timeout(std::time::Duration::from_secs(10)) - .build()?; - - // Creates the job, dispatches it to the correct actor queue, - // and waits for a reply on the dedicated reply queue. - let output = supervisor.run_job_and_await_result(&job).await?; - println!("Synchronous output: {}", output); - - Ok(()) -} -``` - -References used in this flow: -- [rust.SupervisorBuilder::new()](core/supervisor/src/lib.rs:124), [rust.SupervisorBuilder::build()](core/supervisor/src/lib.rs:267) -- [rust.JobBuilder::caller_id()](core/job/src/builder.rs:79), [rust.JobBuilder::context_id()](core/job/src/builder.rs:74), [rust.JobBuilder::script_type()](core/job/src/builder.rs:69), [rust.JobBuilder::script()](core/job/src/builder.rs:84), [rust.JobBuilder::timeout()](core/job/src/builder.rs:94), [rust.JobBuilder::build()](core/job/src/builder.rs:158) -- [rust.Supervisor::create_job()](core/supervisor/src/lib.rs:642), [rust.Supervisor::start_job()](core/supervisor/src/lib.rs:658), [rust.Supervisor::get_job_output()](core/supervisor/src/lib.rs:740) -- [rust.Supervisor::run_job_and_await_result()](core/supervisor/src/lib.rs:672) - - -## 4) Submit a job via the OpenRPC server (Unix IPC or WebSocket) - -The OpenRPC server exposes JSON-RPC 2.0 methods which proxy to the Supervisor: -- Types: [rust.JobParams](interfaces/openrpc/server/src/types.rs:6) -- Methods registered in [interfaces/openrpc/server/src/lib.rs](interfaces/openrpc/server/src/lib.rs:117) - -Unix IPC launcher and client: -- Server: [interfaces/unix/server/src/main.rs](interfaces/unix/server/src/main.rs) -- Client: [interfaces/unix/client/src/main.rs](interfaces/unix/client/src/main.rs) - -Start the IPC server: -```bash -cargo run -p hero-unix-server -- \ - --socket /tmp/baobab.ipc \ - --db-path ./db -``` - -Create a job (JSON-RPC, IPC): -```bash -cargo run -p hero-unix-client -- \ - --socket /tmp/baobab.ipc \ - --method create_job \ - --params '{ - "script": "40 + 2", - "script_type": "SAL", - "caller_id": "02abc...caller", - "context_id": "02def...context", - "timeout": 10 - }' -``` - -This returns the job_id. Then start the job: -```bash -cargo run -p hero-unix-client -- \ - --socket /tmp/baobab.ipc \ - --method start_job \ - --params '[""]' -``` - -Fetch output (optional): -```bash -cargo run -p hero-unix-client -- \ - --socket /tmp/baobab.ipc \ - --method get_job_output \ - --params '[""]' -``` - -Notes: -- The “run_job” JSON-RPC method is present but not fully wired to the full request-reply flow; prefer create_job + start_job + get_job_output for now. -- JobParams fields are defined in [rust.JobParams](interfaces/openrpc/server/src/types.rs:6). - - -## 5) What happens under the hood - -- The job is serialized to Redis under the namespace [rust.NAMESPACE_PREFIX](core/job/src/lib.rs:13) -- The Supervisor picks the actor queue from [rust.ScriptType::actor_queue_suffix()](core/job/src/lib.rs:29) and LPUSHes your job ID -- The actor BLPOPs its queue, loads the job, executes your script, and stores the result back into the Redis job hash -- For synchronous flows, Supervisor waits on a dedicated reply queue until the result arrives via [rust.Supervisor::run_job_and_await_result()](core/supervisor/src/lib.rs:672) - - -## 6) Minimal scripts by actor type - -- OSIS/SAL (Rhai): - - "40 + 2" - - "let x = 21; x * 2" - - You can access injected context variables such as CALLER_ID, CONTEXT_ID (see architecture doc in [core/docs/architecture.md](core/docs/architecture.md)). - -- V/Python (HeroScript): - - Provide a valid HeroScript snippet appropriate for the selected engine and your deployment. - - -## 7) Troubleshooting - -- Ensure Redis is running and reachable at the configured URL -- SAL vs OSIS: pick SAL if your script is blocking/IO-heavy and needs concurrency; otherwise OSIS is fine for sequential non-blocking tasks -- If using OpenRPC IPC, ensure the socket path matches between server and client -- For lifecycle of actors (starting/restarting/health checks), see [core/supervisor/README.md](core/supervisor/README.md) \ No newline at end of file diff --git a/docs/PROJECT_OVERVIEW.md b/docs/PROJECT_OVERVIEW.md deleted file mode 100644 index 880d0d5..0000000 --- a/docs/PROJECT_OVERVIEW.md +++ /dev/null @@ -1,216 +0,0 @@ -# Baobab Project Overview - -This document explains the system architecture and execution model: what a supervisor is, what an actor is (including each actor type and how they are used), how jobs flow through Redis, and how the various interfaces expose functionality over WebSocket and Unix IPC. - -References point directly into the codebase for quick lookup. - - -## 1. Core Concepts - -- Supervisor - - A long-lived orchestrator that: - - Supervises actor lifecycles (start/restart/stop/health checks), - - Dispatches jobs to actors via Redis queues, - - Exposes a high-level API for creating, starting, running, and inspecting jobs. - - Key types and entry points: - - [Supervisor](core/supervisor/src/lib.rs:23) - - [SupervisorBuilder](core/supervisor/src/lib.rs:29) - - [SupervisorBuilder::from_toml()](core/supervisor/src/lib.rs:137) - - [Supervisor::start_actors()](core/supervisor/src/lib.rs:299) - - [Supervisor::run_job_and_await_result()](core/supervisor/src/lib.rs:672) - - [Supervisor::start_job()](core/supervisor/src/lib.rs:658) - - [Supervisor::get_job_status()](core/supervisor/src/lib.rs:705) - - [Supervisor::get_job_output()](core/supervisor/src/lib.rs:740) - - [Supervisor::list_jobs()](core/supervisor/src/lib.rs:761) - - [Supervisor::stop_job()](core/supervisor/src/lib.rs:771) - - [Supervisor::delete_job()](core/supervisor/src/lib.rs:831) - - [Supervisor::clear_all_jobs()](core/supervisor/src/lib.rs:844) - -- Actor - - A worker service that pulls jobs from a Redis queue and executes the job’s script with the appropriate engine/runtime for its type. - - Trait and common loop: - - [Actor](core/actor/src/actor_trait.rs:80) - - [ActorConfig](core/actor/src/actor_trait.rs:41) - - [Actor::spawn() (common loop)](core/actor/src/actor_trait.rs:119) - - [spawn_actor()](core/actor/src/actor_trait.rs:250) - -- Job and Redis schema - - A job encapsulates a unit of work: script, script type (which selects the actor queue), caller/context IDs, timeout, etc. - - Canonical data and status types are re-exported by the supervisor: - - [Job](core/supervisor/src/lib.rs:21) - - [JobStatus](core/supervisor/src/lib.rs:21) - - [ScriptType](core/supervisor/src/lib.rs:21) - - Redis schema used by the supervisor for job supervision is documented in: - - [core/supervisor/README.md](core/supervisor/README.md) - - Keys overview (jobs, actor work queues, reply queues): see lines 95–100 in that file. - - -## 2. Actors and Script Execution - -The system defines four actor types. Each actor has its own queue and executes scripts differently, with standardized context variables injected into script execution (e.g., CALLER_ID, CONTEXT_ID). - -- Design summary: - - [core/docs/architecture.md](core/docs/architecture.md:3) - - [core/docs/architecture.md](core/docs/architecture.md:5) - -Actor types and behavior: - -- OSIS (Rhai, non-blocking, sequential) - - Executes Rhai scripts one after another on a single thread using the Rhai engine. - - Intended for non-blocking tasks. - -- SAL (Rhai, blocking async, concurrent) - - Executes blocking asynchronous Rhai scripts concurrently by spawning a new thread per evaluation. - - Intended for IO-bound or blocking tasks requiring concurrency. - -- V (HeroScript via V engine) and Python (HeroScript via Python engine) - - Execute HeroScript scripts in their respective engines. - -Execution context: - -- Both CALLER_ID and CONTEXT_ID are injected in scope for scripts. See description at: - - [core/docs/architecture.md](core/docs/architecture.md:3) - -Actor implementation surface: - -- Actors implement [Actor](core/actor/src/actor_trait.rs:80) and plug into the provided [Actor::spawn()](core/actor/src/actor_trait.rs:119) loop. -- The common loop: - - Connects to Redis (per-actor id), - - Blocks on the actor’s queue with BLPOP, - - Handles a special “ping” script inline (health check), - - Delegates other jobs to Actor::process_job(). - - -## 3. Supervisor Responsibilities and Guarantees - -- Lifecycle management - - Starts/zinit-registers actors, monitors health, restarts if unhealthy or unresponsive, and cleans up services on shutdown. - - Health checking includes a ping job if idle (actor must respond “pong” immediately). - - Key entry points: - - [Supervisor::start_actors()](core/supervisor/src/lib.rs:299) - - Background lifecycle manager (health loop): - - [Supervisor::spawn_lifecycle_manager()](core/supervisor/src/lib.rs:466) - - Per-actor health handling and restart: - - [Supervisor::check_and_restart_actor()](core/supervisor/src/lib.rs:506) - - Uses zinit as the process manager; see the supervisor readme: - - [core/supervisor/README.md](core/supervisor/README.md) - -- Job supervision - - Create, start, run-and-await, inspect, stop, delete jobs; dispatch based on script type using hardcoded per-type queues: - - [Supervisor::get_actor_queue_key()](core/supervisor/src/lib.rs:410) - - [Supervisor::create_job()](core/supervisor/src/lib.rs:642) - - [Supervisor::start_job()](core/supervisor/src/lib.rs:658) - - [Supervisor::run_job_and_await_result()](core/supervisor/src/lib.rs:672) - - [Supervisor::get_job_status()](core/supervisor/src/lib.rs:705) - - [Supervisor::get_job_output()](core/supervisor/src/lib.rs:740) - - [Supervisor::list_jobs()](core/supervisor/src/lib.rs:761) - - [Supervisor::stop_job()](core/supervisor/src/lib.rs:771) - - [Supervisor::delete_job()](core/supervisor/src/lib.rs:831) - - [Supervisor::clear_all_jobs()](core/supervisor/src/lib.rs:844) - -- Job dependency utilities - - Check prerequisites and update dependents upon completion: - - [Supervisor::check_prerequisites_completed()](core/supervisor/src/lib.rs:862) - - [Supervisor::update_job_status_and_check_dependents()](core/supervisor/src/lib.rs:884) - - [Supervisor::dispatch_ready_jobs()](core/supervisor/src/lib.rs:920) - -- Redis naming and keys (namespace “hero:”) - - See “Redis Schema” section: - - [core/supervisor/README.md](core/supervisor/README.md) - - -## 4. Interfaces (APIs and Transports) - -The project exposes two complementary ways to interact with the supervisor and job system. - -A. OpenRPC Server (JSON-RPC 2.0 over WebSocket or Unix IPC) -- Core types: - - [Transport](interfaces/openrpc/server/src/lib.rs:21) - - [OpenRpcServer](interfaces/openrpc/server/src/lib.rs:37) - - [OpenRpcApi](interfaces/openrpc/server/src/lib.rs:45) -- Server lifecycle: - - [OpenRpcServer::new()](interfaces/openrpc/server/src/lib.rs:98) - - [OpenRpcServer::start()](interfaces/openrpc/server/src/lib.rs:117) -- Methods exposed (selected): - - Authentication: fetch_nonce, authenticate, whoami - - Script execution: play - - Job management: create_job, start_job, run_job, get_job_status, get_job_output, get_job_logs, list_jobs, stop_job, delete_job, clear_all_jobs - - All are registered inside [OpenRpcServer::start()](interfaces/openrpc/server/src/lib.rs:117) using jsonrpsee. -- Transports: - - WebSocket server binding is provided via jsonrpsee when using [Transport::WebSocket](interfaces/openrpc/server/src/lib.rs:21). - - Unix Domain Socket (IPC) is implemented using reth-ipc when using [Transport::Unix](interfaces/openrpc/server/src/lib.rs:21). -- Launchers: - - IPC server binary: - - [interfaces/unix/server/src/main.rs](interfaces/unix/server/src/main.rs) - - IPC client (manual testing tool): - - [interfaces/unix/client/src/main.rs](interfaces/unix/client/src/main.rs) - -B. WebSocket Server (Actix) -- A dedicated Actix-based WebSocket server that runs a multi-circle endpoint: each connected circle uses its path “/{circle_pk}”. Each connection is handled by a dedicated Actix actor. -- Server runtime and session actor: - - [Server](interfaces/websocket/server/src/lib.rs:197) - - Starts HTTP/WS server, binds routes, and spawns the WS actor per connection: - - [Server::spawn_circle_server()](interfaces/websocket/server/src/lib.rs:229) - - per-connection handler: - - [ws_handler()](interfaces/websocket/server/src/lib.rs:688) -- Auth and flow: - - Signature-based auth and session lifecycle are documented in: - - [interfaces/websocket/server/docs/ARCHITECTURE.md](interfaces/websocket/server/docs/ARCHITECTURE.md) - - Nonce issuing, signature verification, and circle membership checks gate protected actions (e.g., play). -- Integration with supervisor: - - The WS server issues job requests via the supervisor (e.g., a “play” call builds and runs a job through [Supervisor](core/supervisor/src/lib.rs:23)). - - -## 5. End-to-End Job Flow - -- Creating and starting a job via the OpenRPC server - - Client calls OpenRPC “create_job”, which builds a [Job](core/supervisor/src/lib.rs:21) and stores it in Redis via [Supervisor::create_job()](core/supervisor/src/lib.rs:642). - - Client then calls “start_job”, which reads the job to determine its [ScriptType](core/supervisor/src/lib.rs:21), computes the actor queue via [Supervisor::get_actor_queue_key()](core/supervisor/src/lib.rs:410), and pushes the job ID to the actor’s Redis list via [Supervisor::start_job()](core/supervisor/src/lib.rs:658). - -- Running-and-awaiting a job in one step - - Client calls “run_job” or equivalent flow; the server uses [Supervisor::run_job_and_await_result()](core/supervisor/src/lib.rs:672): - - Stores the job, - - Pushes to the appropriate actor queue, - - Waits for the result on a dedicated reply queue “hero::reply:{job_id}”. - -- Actor processing loop - - The actor BLPOP’s its queue (timeout), receives a job ID, loads the job, handles “ping” inline, otherwise calls [Actor::process_job()](core/actor/src/actor_trait.rs:80) for execution, and writes status/output back to Redis. - - The common loop is provided by [Actor::spawn()](core/actor/src/actor_trait.rs:119). - -- Health checks - - The supervisor periodically checks zinit state and may issue ping jobs if idle; failure to respond leads to restart. See lifecycle logic: - - [Supervisor::spawn_lifecycle_manager()](core/supervisor/src/lib.rs:466) - - [Supervisor::check_and_restart_actor()](core/supervisor/src/lib.rs:506) - -- Redis schema pointers (namespace hero:) - - See section “Redis Schema for Job Supervision”: - - [core/supervisor/README.md](core/supervisor/README.md) - - -## 6. How the Interfaces Fit Together - -- The OpenRPC server provides a JSON-RPC 2.0 façade for programmatic control (automation, services). - - Choose between WebSocket and Unix IPC transports via [Transport](interfaces/openrpc/server/src/lib.rs:21). - - It wraps the [Supervisor](core/supervisor/src/lib.rs:23), delegating all job and lifecycle supervision calls. - -- The WebSocket (Actix) server provides a multi-circle, session-based, interactive API well-suited for browser or persistent WS clients. - - It authenticates users per-circle, then issues supervisor-backed job calls within the authenticated context. - - Session isolation is per WS actor instance; see: - - [interfaces/websocket/server/docs/ARCHITECTURE.md](interfaces/websocket/server/docs/ARCHITECTURE.md) - -Both interfaces ultimately converge on the same core abstraction: the [Supervisor](core/supervisor/src/lib.rs:23) orchestrating jobs and actors over Redis with zinit-backed lifecycle guarantees. - - -## 7. Additional References - -- Architecture summary for actor types and scripting: - - [core/docs/architecture.md](core/docs/architecture.md) - -- Supervisor documentation and prerequisites (Redis, zinit): - - [core/supervisor/README.md](core/supervisor/README.md) - -- TUI/CLI examples and lifecycle demos: - - [core/supervisor/examples](core/supervisor/examples) - -- Actor README (queue consumption, Rhai execution, context variables): - - [core/actor/README.md](core/actor/README.md) \ No newline at end of file diff --git a/docs/REDIS_QUEUES_GUIDE.md b/docs/REDIS_QUEUES_GUIDE.md deleted file mode 100644 index be08f7d..0000000 --- a/docs/REDIS_QUEUES_GUIDE.md +++ /dev/null @@ -1,199 +0,0 @@ -# Redis Queues Guide: Who Pushes Where, When, and How to Inspect - -This guide documents the canonical queues used in the project, explains which component pushes to which queue at each step, and provides redis-cli commands to inspect state during development. - -Canonical keys -- Job hash (immutable key shape): - - hero:job:{job_id} - - Builder: [rust.keys::job_hash()](core/job/src/lib.rs:396) -- Work queues (push here to dispatch work): - - Type queue: hero:q:work:type:{script_type} - - Builders: - - [rust.keys::work_type()](core/job/src/lib.rs:405) - - [rust.keys::work_group()](core/job/src/lib.rs:411) - - [rust.keys::work_instance()](core/job/src/lib.rs:420) -- Reply queue (optional, for actors that send explicit replies): - - hero:q:reply:{job_id} - - Builder: [rust.keys::reply()](core/job/src/lib.rs:401) -- Control queue (optional stop/control per-type): - - hero:q:ctl:type:{script_type} - - Builder: [rust.keys::stop_type()](core/job/src/lib.rs:429) - - -1) Who pushes where - -A. Supervisor: creating, starting, and running jobs -- Create job (stores job hash): - - [rust.Supervisor::create_job()](core/supervisor/src/lib.rs:660) - - Persists hero:job:{job_id} via [rust.Job::store_in_redis()](core/job/src/lib.rs:147) -- Start job (dispatch to worker queue): - - [rust.Supervisor::start_job()](core/supervisor/src/lib.rs:675) → [rust.Supervisor::start_job_using_connection()](core/supervisor/src/lib.rs:599) - - LPUSH hero:q:work:type:{script_type} using [rust.keys::work_type()](core/job/src/lib.rs:405) -- Run-and-wait (one-shot): - - [rust.Supervisor::run_job_and_await_result()](core/supervisor/src/lib.rs:689) - - Stores hero:job:{job_id}, LPUSH hero:q:work:type:{script_type} (same as start) - - Waits on hero:q:reply:{job_id} (via [rust.keys::reply()](core/job/src/lib.rs:401)) and also polls hero:job:{job_id} for output to support hash-only actors - -B. Terminal UI: quick dispatch from the actor TUI -- Stores job using Job::store_in_redis, then pushes to type queue: - - Dispatch code: [core/actor/src/terminal_ui.rs](core/actor/src/terminal_ui.rs:460) - - LPUSH hero:q:work:type:{script_type} using [rust.keys::work_type()](core/job/src/lib.rs:405) - -C. Actors: consuming and completing work -- Consume jobs: - - Standalone Rhai actor: [rust.spawn_rhai_actor()](core/actor/src/lib.rs:211) - - BLPOP hero:q:work:type:{script_type} (queue selection computed via [rust.derive_script_type_from_actor_id()](core/actor/src/lib.rs:262), then [rust.keys::work_type()](core/job/src/lib.rs:405)) - - Trait-based actor loop: [rust.Actor::spawn()](core/actor/src/actor_trait.rs:119) - - BLPOP hero:q:work:type:{script_type} using [rust.keys::work_type()](core/job/src/lib.rs:405) -- Write results: - - Hash-only (current default): [rust.Job::set_result()](core/job/src/lib.rs:322) updates hero:job:{job_id} with output and status=finished - - Optional reply queue model: actor may LPUSH hero:q:reply:{job_id} (if implemented) - - -2) End-to-end flows and the queues involved - -Flow A: Two-step (create + start) with Supervisor -- Code path: - - [rust.Supervisor::create_job()](core/supervisor/src/lib.rs:660) - - [rust.Supervisor::start_job()](core/supervisor/src/lib.rs:675) -- Keys touched: - - hero:job:{job_id} (created) - - hero:q:work:type:{script_type} (LPUSH job_id) -- Expected actor behavior: - - BLPOP hero:q:work:type:{script_type} - - Execute script, then [rust.Job::set_result()](core/job/src/lib.rs:322) -- How to inspect with redis-cli: - - FLUSHALL (fresh dev) then run create and start - - Verify job hash: - - HGETALL hero:job:{job_id} - - Verify queue length before consumption: - - LLEN hero:q:work:type:osis - - See pending items: - - LRANGE hero:q:work:type:osis 0 -1 - - After actor runs, verify result in job hash: - - HGET hero:job:{job_id} status - - HGET hero:job:{job_id} output - -Flow B: One-shot (run and await result) with Supervisor -- Code path: - - [rust.Supervisor::run_job_and_await_result()](core/supervisor/src/lib.rs:689) - - Uses [rust.keys::reply()](core/job/src/lib.rs:401) and polls the hash for output -- Keys touched: - - hero:job:{job_id} - - hero:q:work:type:{script_type} - - hero:q:reply:{job_id} (only if an actor uses reply queues) -- How to inspect with redis-cli: - - While waiting: - - LLEN hero:q:work:type:osis - - HGET hero:job:{job_id} status - - If an actor uses reply queues (optional): - - LLEN hero:q:reply:{job_id} - - LRANGE hero:q:reply:{job_id} 0 -1 - - After completion: - - HGET hero:job:{job_id} output - -Flow C: Dispatch from the Actor TUI (manual testing) -- Code path: - - [core/actor/src/terminal_ui.rs](core/actor/src/terminal_ui.rs:460) stores job and LPUSH to [rust.keys::work_type()](core/job/src/lib.rs:405) -- Keys touched: - - hero:job:{job_id} - - hero:q:work:type:{script_type} -- How to inspect with redis-cli: - - List all work queues: - - KEYS hero:q:work:type:* - - Show items in a specific type queue: - - LRANGE hero:q:work:type:osis 0 -1 - - Read one pending job: - - HGETALL hero:job:{job_id} - - After actor runs: - - HGET hero:job:{job_id} status - - HGET hero:job:{job_id} output - - -3) Example redis-cli sequences - -A. Basic OSIS job lifecycle (two-step) -- Prepare - - FLUSHALL -- Create and start (via code or supervisor-cli) -- Inspect queue and job - - KEYS hero:q:work:type:* - - LLEN hero:q:work:type:osis - - LRANGE hero:q:work:type:osis 0 -1 - - HGETALL hero:job:{job_id} -- After actor consumes the job: - - HGET hero:job:{job_id} status → finished - - HGET hero:job:{job_id} output → script result - - LLEN hero:q:work:type:osis → likely 0 if all consumed - -B. One-shot run-and-wait (hash-only actor) -- Prepare - - FLUSHALL -- Submit via run_job_and_await_result() -- While supervisor waits: - - HGET hero:job:{job_id} status → started/finished - - (Optional) LLEN hero:q:reply:{job_id} → typically 0 if actor doesn’t use reply queues -- When done: - - HGET hero:job:{job_id} output → result - -C. Listing and cleanup helpers -- List jobs - - KEYS hero:job:* -- Show a specific job - - HGETALL hero:job:{job_id} -- Clear all keys (dev only) - - FLUSHALL - - -4) Where the queue names are computed in code - -- Builders for canonical keys: - - [rust.keys::job_hash()](core/job/src/lib.rs:396) - - [rust.keys::reply()](core/job/src/lib.rs:401) - - [rust.keys::work_type()](core/job/src/lib.rs:405) - - [rust.keys::work_group()](core/job/src/lib.rs:411) - - [rust.keys::work_instance()](core/job/src/lib.rs:420) -- Supervisor routing and waiting: - - Type queue selection: [rust.Supervisor::get_actor_queue_key()](core/supervisor/src/lib.rs:410) - - LPUSH to type queue: [rust.Supervisor::start_job_using_connection()](core/supervisor/src/lib.rs:599) - - One-shot run and wait: [rust.Supervisor::run_job_and_await_result()](core/supervisor/src/lib.rs:689) -- Actor consumption: - - Standalone Rhai actor: [rust.spawn_rhai_actor()](core/actor/src/lib.rs:211) - - Type queue computed via [rust.derive_script_type_from_actor_id()](core/actor/src/lib.rs:262) + [rust.keys::work_type()](core/job/src/lib.rs:405) - - Trait-based actor loop: [rust.Actor::spawn()](core/actor/src/actor_trait.rs:119) - - BLPOP type queue via [rust.keys::work_type()](core/job/src/lib.rs:405) - - -5) Quick checklist for debugging - -- Nothing consumes from the type queue - - Is at least one actor process running that BLPOPs hero:q:work:type:{script_type}? - - LLEN hero:q:work:type:{script_type} shows > 0 means unconsumed backlog -- Job “Dispatched” but never “Finished” - - HGET hero:job:{job_id} status - - Actor logs: check for script errors and verify it is connected to the same Redis -- “run-and-wait” timeout - - Hash-only actors don’t push to reply queues; the supervisor will still return once it sees hero:job:{job_id}.output set by [rust.Job::set_result()](core/job/src/lib.rs:322) -- Mixed types: - - Verify you targeted the correct type queue (e.g., osis vs sal): LLEN hero:q:work:type:osis, hero:q:work:type:sal - - -6) Canonical patterns to remember - -- To dispatch a job: - - LPUSH hero:q:work:type:{script_type} {job_id} -- To read job data: - - HGETALL hero:job:{job_id} -- To wait for output (optional reply model): - - BLPOP hero:q:reply:{job_id} {timeout_secs} -- To verify system state: - - KEYS hero:q:* - - KEYS hero:job:* - - -This guide reflects the canonical scheme implemented in: -- [rust.Supervisor](core/supervisor/src/lib.rs:1) -- [rust.keys](core/job/src/lib.rs:392) -- [core/actor/src/lib.rs](core/actor/src/lib.rs:1) -- [core/actor/src/actor_trait.rs](core/actor/src/actor_trait.rs:1) -- [core/actor/src/terminal_ui.rs](core/actor/src/terminal_ui.rs:1) \ No newline at end of file diff --git a/docs/REDIS_QUEUES_NAMING_PROPOSAL.md b/docs/REDIS_QUEUES_NAMING_PROPOSAL.md deleted file mode 100644 index ca87207..0000000 --- a/docs/REDIS_QUEUES_NAMING_PROPOSAL.md +++ /dev/null @@ -1,231 +0,0 @@ -# Redis Queue Naming Proposal (Multi-Actor, Multi-Type, Scalable) - -Goal -- Define a consistent, future-proof Redis naming scheme that: - - Supports multiple actor types (OSIS, SAL, V, Python) - - Supports multiple pools/groups and instances per type - - Enables fair load-balancing and targeted dispatch - - Works with both “hash-output” actors and “reply-queue” actors - - Keeps migration straightforward from the current keys - -Motivation -- Today, multiple non-unified patterns exist: - - Per-actor keys like "hero:job:{actor_id}" consumed by in-crate Rhai actor - - Per-type keys like "hero:job:actor_queue:{suffix}" used by other components - - Protocol docs that reference "hero:work_queue:{actor_id}" and "hero:reply:{job_id}" -- This fragmentation causes stuck “Dispatched” jobs when the LPUSH target doesn’t match the BLPOP listener. We need one canonical scheme, with well-defined fallbacks. - - -## 1) Canonical Key Names - -Prefix conventions -- Namespace prefix: hero: -- All queues collected under hero:q:* to separate from job hashes hero:job:* -- All metadata under hero:meta:* for discoverability - -Job and result keys -- Job hash (unchanged): hero:job:{job_id} -- Reply queue: hero:q:reply:{job_id} - -Work queues (new canonical) -- Type queue (shared): hero:q:work:type:{script_type} - - Examples: - - hero:q:work:type:osis - - hero:q:work:type:sal - - hero:q:work:type:v - - hero:q:work:type:python -- Group queue (optional, shared within a group): hero:q:work:type:{script_type}:group:{group} - - Examples: - - hero:q:work:type:osis:group:default - - hero:q:work:type:sal:group:io -- Instance queue (most specific, used for targeted dispatch): hero:q:work:type:{script_type}:group:{group}:inst:{instance} - - Examples: - - hero:q:work:type:osis:group:default:inst:1 - - hero:q:work:type:sal:group:io:inst:3 - -Control queues (optional, future) -- Stop/control per-type: hero:q:ctl:type:{script_type} -- Stop/control per-instance: hero:q:ctl:type:{script_type}:group:{group}:inst:{instance} - -Actor presence and metadata -- Instance presence (ephemeral, with TTL refresh): hero:meta:actor:inst:{script_type}:{group}:{instance} - - Value: JSON { pid, hostname, started_at, version, capabilities, last_heartbeat } - - Used by the supervisor to discover live consumers and to select targeted queueing - - -## 2) Dispatch Strategy - -- Default: Push to the Type queue hero:q:work:type:{script_type} - - Allows N instances to BLPOP the same shared queue (standard fan-out). -- Targeted: If user or scheduler specifies a group and/or instance, push to the most specific queue - - Instance queue (highest specificity): - - hero:q:work:type:{script_type}:group:{group}:inst:{instance} - - Else Group queue: - - hero:q:work:type:{script_type}:group:{group} - - Else Type queue (fallback): - - hero:q:work:type:{script_type} -- Priority queues (optional extension): - - Append :prio:{level} to any of the above - - Actors BLPOP a list of queues in priority order - -Example routing -- No group/instance specified: - - LPUSH hero:q:work:type:osis {job_id} -- Group specified ("default"), no instance: - - LPUSH hero:q:work:type:osis:group:default {job_id} -- Specific instance: - - LPUSH hero:q:work:type:osis:group:default:inst:2 {job_id} - - -## 3) Actor Consumption Strategy - -- Actor identifies itself with: - - script_type (osis/sal/v/python) - - group (defaults to "default") - - instance number (unique within group) -- Actor registers presence: - - SET hero:meta:actor:inst:{script_type}:{group}:{instance} {...} EX 15 - - Periodically refresh to act as heartbeat -- Actor BLPOP order: - 1) Instance queue (most specific) - 2) Group queue - 3) Type queue -- This ensures targeted jobs are taken first (if any), otherwise fall back to group or shared type queue. -- Actors that implement reply-queue semantics will also LPUSH to hero:q:reply:{job_id} on completion. Others just update hero:job:{job_id} with status+output. - - -## 4) Backward Compatibility And Migration - -- During transition, Supervisor can LPUSH to both: - - New canonical queues (hero:q:work:type:...) - - Selected legacy queues (hero:job:actor_queue:{suffix}, hero:job:{actor_id}, hero:work_queue:...) -- Actors: - - Update actors to BLPOP the canonical queues first, then legacy fallback -- Phased plan: - 1) Introduce canonical queues alongside legacy; Supervisor pushes to both (compat mode) - 2) Switch actors to consume canonical first - 3) Deprecate legacy queues and remove dual-push -- No change to job hashes hero:job:{job_id} - - -## 5) Required Code Changes (by file) - -Supervisor (routing and reply queue) -- Replace queue computation with canonical builder: - - [rust.Supervisor::get_actor_queue_key()](core/supervisor/src/lib.rs:410) - - Change to build canonical keys given script_type (+ optional group/instance from Job or policy) -- Update start logic to LPUSH to canonical queue(s): - - [rust.Supervisor::start_job_using_connection()](core/supervisor/src/lib.rs:599) - - Use only canonical queue(s). In migration phase, also LPUSH legacy queues. -- Standardize reply queue name: - - [rust.Supervisor::run_job_and_await_result()](core/supervisor/src/lib.rs:689) - - Use hero:q:reply:{job_id} - - Keep “poll job hash” fallback for actors that don’t use reply queues -- Stop queue naming: - - [rust.Supervisor::stop_job()](core/supervisor/src/lib.rs:789) - - Use hero:q:ctl:type:{script_type} in canonical mode - -Actor (consumption and presence) -- In-crate Rhai actor: - - Queue key construction and BLPOP list: - - [rust.spawn_rhai_actor()](core/actor/src/lib.rs:211) - - Current queue_key at [core/actor/src/lib.rs:220] - - Replace single-queue BLPOP with multi-key BLPOP in priority order: - 1) hero:q:work:type:{script_type}:group:{group}:inst:{instance} - 2) hero:q:work:type:{script_type}:group:{group} - 3) hero:q:work:type:{script_type} - - For migration, optionally include legacy queues last. - - Presence registration (periodic SET with TTL): - - Add at actor startup and refresh on loop tick -- For actors that implement reply queues: - - After finishing job, LPUSH hero:q:reply:{job_id} {result} - - For hash-only actors, continue to call [rust.Job::set_result()](core/job/src/lib.rs:322) - -Shared constants (avoid string drift) -- Introduce constants and helpers in a central crate (hero_job) to build keys consistently: - - fn job_hash_key(job_id) -> "hero:job:{job_id}" - - fn reply_queue_key(job_id) -> "hero:q:reply:{job_id}" - - fn work_queue_type(script_type) -> "hero:q:work:type:{type}" - - fn work_queue_group(script_type, group) -> "hero:q:work:type:{type}:group:{group}" - - fn work_queue_instance(script_type, group, inst) -> "hero:q:work:type:{type}:group:{group}:inst:{inst}" -- Replace open-coded strings in: - - [rust.Supervisor](core/supervisor/src/lib.rs:1) - - [rust.Actor code](core/actor/src/lib.rs:1) - - Any CLI/TUI or interface components that reference queues - -Interfaces -- OpenRPC/WebSocket servers do not need to know queue names; they call Supervisor API. No changes except to follow the Supervisor’s behavior for “run-and-wait” vs “create+start+get_output” flows. - - -## 6) Example Scenarios - -Scenario A: Single OSIS pool with two instances -- Actors: - - osis group=default inst=1 - - osis group=default inst=2 -- Incoming job (no targeting): - - LPUSH hero:q:work:type:osis {job_id} -- Actors BLPOP order: - - inst queue - - group queue - - type queue (this one will supply) -- Effective result: classic round-robin-like behavior, two workers share load. - -Scenario B: SAL pool “io” with instance 3; targeted dispatch -- Job sets target group=io and instance=3 -- Supervisor LPUSH hero:q:work:type:sal:group:io:inst:3 {job_id} -- Only that instance consumes it, enabling pinning to a specific worker. - -Scenario C: Mixed old and new actors (migration window) -- Supervisor pushes to canonical queue(s) and to a legacy queue hero:job:actor_queue:osis -- New actors consume canonical queues -- Legacy actors consume legacy queue -- No job is stuck; both ecosystems coexist until the legacy path is removed. - - -## 7) Phased Migration Plan - -Phase 0 (Docs + helpers) -- Add helpers in hero_job to compute keys (see “Shared constants”) -- Document the new scheme and consumption order (this file) - -Phase 1 (Supervisor) -- Update [rust.Supervisor::get_actor_queue_key()](core/supervisor/src/lib.rs:410) and [rust.Supervisor::start_job_using_connection()](core/supervisor/src/lib.rs:599) to use canonical queues -- Keep dual-push to legacy queues behind a feature flag or config for rollout -- Standardize reply queue to hero:q:reply:{job_id} in [rust.Supervisor::run_job_and_await_result()](core/supervisor/src/lib.rs:689) - -Phase 2 (Actors) -- Update [rust.spawn_rhai_actor()](core/actor/src/lib.rs:211) to BLPOP from canonical queues in priority order and to register presence keys -- Optionally emit reply to hero:q:reply:{job_id} in addition to hash-based result (feature flag) - -Phase 3 (Cleanup) -- After all actors and Supervisor deployments are updated and stable, remove the legacy dual-push and fallback consume paths - - -## 8) Optional Enhancements - -- Priority queues: - - Suffix queues with :prio:{0|1|2}; actors BLPOP [inst prio0, group prio0, type prio0, inst prio1, group prio1, type prio1, ...] -- Rate limiting/back-pressure: - - Use metadata to signal busy state or reported in-flight jobs; Supervisor can target instance queues accordingly. -- Resilience: - - Move to Redis Streams for job event logs; lists remain fine for simple FIFO processing. -- Observability: - - hero:meta:actor:* and hero:meta:queue:stats:* to keep simple metrics for dashboards. - - -## 9) Summary - -- Canonicalize to hero:q:work:type:{...} (+ group, + instance), and hero:q:reply:{job_id} -- Actors consume instance → group → type -- Supervisor pushes to most specific queue available, defaulting to type -- Provide helpers to build keys and remove ad-hoc string formatting -- Migrate with a dual-push (canonical + legacy) phase to avoid downtime - -Proposed touchpoints to implement (clickable references) -- [rust.Supervisor::get_actor_queue_key()](core/supervisor/src/lib.rs:410) -- [rust.Supervisor::start_job_using_connection()](core/supervisor/src/lib.rs:599) -- [rust.Supervisor::run_job_and_await_result()](core/supervisor/src/lib.rs:689) -- [rust.spawn_rhai_actor()](core/actor/src/lib.rs:211) -- [core/actor/src/lib.rs](core/actor/src/lib.rs:220) -- [rust.Job::set_result()](core/job/src/lib.rs:322) \ No newline at end of file diff --git a/docs/RPC_IMPLEMENTATION.md b/docs/RPC_IMPLEMENTATION.md deleted file mode 100644 index 5d741e1..0000000 --- a/docs/RPC_IMPLEMENTATION.md +++ /dev/null @@ -1,124 +0,0 @@ -# RPC Implementation (jsonrpsee) for Supervisor - -Objective -- Provide an HTTP/WS JSON-RPC server with jsonrpsee that exposes all Supervisor job operations. -- Use the current Supervisor and job model directly; methods should map 1:1 to Supervisor APIs. -- Keep the implementation simple: a single transport (jsonrpsee::server::Server on SocketAddr). - -Canonical model -- Jobs are stored and updated in Redis under hero:job:{job_id}. -- Work is dispatched to type queues hero:q:work:type:{script_type}. -- Actors consume by script type and update the job hash status/output. -- Server-side types and queues are already aligned in code (see keys in [rust.keys](core/job/src/lib.rs:392)). - -What exists today (summary) -- Server state and registry - - [rust.OpenRpcServer](interfaces/openrpc/server/src/lib.rs:37) holds a Supervisor inside RwLock. - - Methods are registered manually with jsonrpsee::RpcModule in [rust.OpenRpcServer::start()](interfaces/openrpc/server/src/lib.rs:117). -- Methods wired vs. stubbed - - Wired: create_job, start_job, get_job_status, get_job_output, stop_job, delete_job, clear_all_jobs. - - Stubbed or partial: run_job (returns a formatted string), play (returns canned output), get_job_logs (mocked), list_jobs (returns fabricated Job objects from IDs). -- Transports - - start() supports a Unix transport through reth-ipc and a WebSocket SocketAddr. We only need HTTP/WS via jsonrpsee::server::Server::builder().build(addr). - -Target surface (final) -- Methods - - fetch_nonce(pubkey: String) -> String [optional now] - - authenticate(pubkey: String, signature: String, nonce: String) -> bool [optional now] - - whoami() -> String [optional now] - - play(script: String) -> PlayResult { output: String } [maps to run_job with a chosen default ScriptType] - - create_job(job: JobParams) -> String (job_id) - - start_job(job_id: String) -> { success: bool } - - run_job(script: String, script_type: ScriptType, prerequisites?: Vec) -> String (output) - - get_job_status(job_id: String) -> JobStatus - - get_job_output(job_id: String) -> String - - get_job_logs(job_id: String) -> JobLogsResult { logs: String | null } - - list_jobs() -> Vec - - stop_job(job_id: String) -> null - - delete_job(job_id: String) -> null - - clear_all_jobs() -> null -- Types - - ScriptType = OSIS | SAL | V | Python ([rust.ScriptType](core/job/src/lib.rs:16)) - - JobParams: script, script_type, caller_id, context_id, timeout?, prerequisites? - - JobStatus: Dispatched | WaitingForPrerequisites | Started | Error | Finished - - DTOs in [rust.interfaces/openrpc/server/src/types.rs](interfaces/openrpc/server/src/types.rs:1) - -Required changes - -1) Transport: simplify to HTTP/WS on SocketAddr -- Remove Unix transport: in [rust.OpenRpcServer::start()](interfaces/openrpc/server/src/lib.rs:247), delete Transport::Unix and reth-ipc usage. -- Use jsonrpsee::server::Server::builder().build(addr) and server.start(module), per upstream examples: - - [rust.http](reference_jsonrpsee_crate_examples/http.rs:53) - - [rust.ws](reference_jsonrpsee_crate_examples/ws.rs:55) - -2) ScriptType consistency end-to-end -- Ensure ScriptType is hero_job::ScriptType (OSIS | SAL | V | Python) in request/response types (already used in [rust.JobParams](interfaces/openrpc/server/src/types.rs:6)). If openrpc.json is used to generate docs or clients, update its enum to match. - -3) Implement run_job (one-shot) -- In [rust.OpenRpcApiServer::run_job](interfaces/openrpc/server/src/lib.rs:366): - - Build a hero_job::JobBuilder with caller_id/context_id placeholders (or accept them as parameters later). - - Set script, script_type, optional prerequisites, timeout default. - - Call supervisor.run_job_and_await_result(&job) and return the output string. - -4) Implement play as a thin wrapper -- In [rust.OpenRpcApiServer::play](interfaces/openrpc/server/src/lib.rs:304): - - Choose a default ScriptType (recommendation: SAL), then delegate to run_job(script, SAL, None). - - Return PlayResult { output }. - -5) Implement get_job_logs via Supervisor -- Replace the mocked return in [rust.get_job_logs](interfaces/openrpc/server/src/lib.rs:400) with a call to: - - supervisor.get_job_logs(&job_id) -> Option and wrap into JobLogsResult { logs }. - -6) list_jobs should return Vec (IDs only) -- Replace placeholder construction in [rust.list_jobs](interfaces/openrpc/server/src/lib.rs:407) with: - - supervisor.list_jobs() returning Vec directly. -- Optionally add get_job(job_id) later if needed. - -7) Error handling -- Map SupervisorError to jsonrpsee error codes: - - Invalid input → ErrorCode::InvalidParams - - Timeout → a custom code or InvalidParams; optionally use -32002 as a custom timeout code. - - Internal IO/Redis errors → ErrorCode::InternalError -- Keep server logs descriptive; return minimal error messages to clients. - -8) Server lifecycle -- Keep OpenRpcServer::new() to build with TOML or builder defaults (see [rust.OpenRpcServer::new()](interfaces/openrpc/server/src/lib.rs:98)). -- Expose a “start_on(addr)” function that returns a ServerHandle (just like upstream examples). -- Optional: expose Supervisor::start_rpc_server(host, port) to own lifecycle from Supervisor; or leave it in interfaces/openrpc with a thin cmd binary to start it. - -Non-goals (for this phase) -- Unix IPC transport (reth-ipc). -- Advanced middleware (CORS, host filters, rate-limiting). -- RPC auth flows (fetch_nonce/authenticate/whoami) beyond placeholders. -- Pub/Sub over RPC. - -Reference mapping (clickable) -- Server core and methods: - - [rust.OpenRpcServer](interfaces/openrpc/server/src/lib.rs:37) - - [rust.OpenRpcApi](interfaces/openrpc/server/src/lib.rs:45) - - [rust.OpenRpcServer::start()](interfaces/openrpc/server/src/lib.rs:117) - - [rust.JobParams](interfaces/openrpc/server/src/types.rs:6) - - [rust.StartJobResult](interfaces/openrpc/server/src/types.rs:23) - - [rust.JobLogsResult](interfaces/openrpc/server/src/types.rs:29) -- Supervisor backend: - - [rust.Supervisor::create_job()](core/supervisor/src/lib.rs:660) - - [rust.Supervisor::start_job()](core/supervisor/src/lib.rs:675) - - [rust.Supervisor::run_job_and_await_result()](core/supervisor/src/lib.rs:689) - - [rust.Supervisor::get_job_status()](core/supervisor/src/lib.rs:723) - - [rust.Supervisor::get_job_output()](core/supervisor/src/lib.rs:758) - - [rust.Supervisor::get_job_logs()](core/supervisor/src/lib.rs:817) - - [rust.Supervisor::list_jobs()](core/supervisor/src/lib.rs:780) - - [rust.Supervisor::stop_job()](core/supervisor/src/lib.rs:789) - - [rust.Supervisor::delete_job()](core/supervisor/src/lib.rs:850) - - [rust.Supervisor::clear_all_jobs()](core/supervisor/src/lib.rs:862) -- jsonrpsee examples to replicate transport and registration patterns: - - HTTP: [rust.http example](reference_jsonrpsee_crate_examples/http.rs:53) - - WS: [rust.ws example](reference_jsonrpsee_crate_examples/ws.rs:55) - -Acceptance checklist -- Server starts on a host:port using jsonrpsee::server::Server. -- All Supervisor operations callable over RPC, 1:1 mapping, returning correct DTOs. -- ScriptType uses OSIS|SAL|V|Python. -- list_jobs returns Vec and no fake job objects. -- run_job and play perform real execution and return actual outputs. -- No Unix IPC code path remains in start(). \ No newline at end of file