From 8d1588aaf78866bb19501a4177558898289aedb5 Mon Sep 17 00:00:00 2001 From: Zachery Aaron Shores-Chmielewski Date: Tue, 28 Jul 2026 13:04:13 +0400 Subject: [PATCH] refactor: final filetree shape Reorganize crates/mvp-system from flat files into domain module trees (chat, node, node_data, observability, orchestration, prompt, staging, transport, worker) with documented mod.rs boundaries, and drop the stale inline spec docs. - lib.rs: replace ~20 flat mod declarations with one pub-mod-per-domain (chat/node/node_data/observability/orchestration/prompt/staging/transport/worker) - node/, chat/, observability/, orchestration/, staging/, prompt/, transport/, worker/: add mod.rs files with module-boundary doc comments and re-exports (e.g. chat re-exports run_from_args; orchestration re-exports RunConfig/RunId/GgufSource/TokenizerSource/ProviderKind) - orchestration: group providers under provider_adapters/{docker_cluster,relay,vastai} and fold engine_builder/, config, run_fsm, run_plan, provisioning, resource_inventory, membership_readiness, and token_endpoint under orchestration/ - transport: consolidate codec registration into transport/codec_registry::register_mvp_actor_codecs (was crate::actors::register_mvp_actor_codecs) and rename actors/codec.rs to transport/json_codec.rs - rename and relocate files into their domains (arena_manager->node_data/arena, actors/node_agent->node/actor, actors/orchestrator->orchestration/actor, stage_controller->staging/actor, telemetry/dashboard_view/etc->observability/, benchmark_observability->observability::benchmark, edge_establisher->node::edge_lifecycle, prompt_rpc->prompt::rpc) and update all crate:: imports accordingly - remove the stale crates/mvp-system/specs/*.md (MVP_SYSTEM_MODULE_BOUNDARY_SPEC, mvp_chat, orchestrator) now that module boundaries live in mod.rs docs Signed-off-by: Zachery Aaron Shores-Chmielewski --- .../specs/MVP_SYSTEM_MODULE_BOUNDARY_SPEC.md | 680 ------------- crates/mvp-system/specs/mvp_chat.md | 798 --------------- crates/mvp-system/specs/orchestrator.md | 954 ------------------ crates/mvp-system/src/actors/mod.rs | 17 - crates/mvp-system/src/chat/config.rs | 7 + .../mvp-system/src/{chat.rs => chat/mod.rs} | 2 + .../mvp-system/src/{ => chat}/node_image.rs | 1 + crates/mvp-system/src/chat/runtime.rs | 14 +- crates/mvp-system/src/lib.rs | 18 - crates/mvp-system/src/node.rs | 22 - .../{actors/node_agent.rs => node/actor.rs} | 4 +- .../boot_lifecycle.rs} | 0 .../edge_lifecycle.rs} | 0 crates/mvp-system/src/node/mod.rs | 9 + .../src/node/worker_node_runtime.rs | 12 +- crates/mvp-system/src/node_data.rs | 42 - .../{arena_manager.rs => node_data/arena.rs} | 0 .../edge_actor.rs} | 0 .../ingress.rs} | 0 crates/mvp-system/src/node_data/mod.rs | 25 + crates/mvp-system/src/observability.rs | 19 - .../benchmark.rs} | 0 .../src/{ => observability}/dashboard_view.rs | 2 +- .../src/observability/frame_archive.rs | 4 +- .../lifecycle.rs} | 0 crates/mvp-system/src/observability/mod.rs | 8 + .../provisioning_logs.rs} | 0 .../src/{ => observability}/telemetry.rs | 4 +- .../actor.rs} | 2 +- crates/mvp-system/src/orchestration/app.rs | 20 +- .../src/{ => orchestration}/config.rs | 93 +- .../engine_builder/engine.rs | 0 .../engine_builder/error.rs | 0 .../engine_builder/events.rs | 0 .../engine_builder/launcher.rs | 0 .../{ => orchestration}/engine_builder/mod.rs | 0 .../engine_builder/model.rs | 0 .../engine_builder/node_image.rs | 0 .../engine_builder/planner.rs | 0 .../engine_builder/pool.rs | 0 .../engine_builder/roles.rs | 0 .../engine_builder/runtime_stack.rs | 6 +- .../engine_builder/workload.rs | 0 .../mod.rs} | 11 +- .../provider_adapters/vastai/config.rs | 83 ++ .../{vastai.rs => vastai/mod.rs} | 4 +- .../src/orchestration/provisioning.rs | 2 +- .../{ => orchestration}/resource_inventory.rs | 0 .../src/{prompt.rs => prompt/mod.rs} | 6 +- .../src/{prompt_rpc.rs => prompt/rpc.rs} | 2 +- .../stage_controller.rs => staging/actor.rs} | 0 .../src/{staging.rs => staging/mod.rs} | 4 +- .../tests/bootstrap_datastream_guarantees.rs | 2 +- .../src/tests/driver_pumps_guarantees.rs | 2 +- .../src/tests/edge_establisher_guarantees.rs | 2 +- .../src/tests/local_mock/assertions.rs | 2 +- .../src/tests/local_mock/environment.rs | 2 +- .../tests/local_mock_pipeline_integration.rs | 2 +- .../src/tests/module_surface_guarantees.rs | 19 +- .../tests/node_boot_lifecycle_guarantees.rs | 2 +- .../tests/observability_surface_guarantees.rs | 2 +- .../tests/resource_inventory_guarantees.rs | 2 +- .../src/tests/telemetry_guarantees.rs | 2 +- crates/mvp-system/src/transport.rs | 13 - .../src/transport/codec_registry.rs | 11 + .../src/{ => transport}/driver_pumps.rs | 0 .../{ => transport}/endpoint_advertisement.rs | 0 .../codec.rs => transport/json_codec.rs} | 0 crates/mvp-system/src/transport/mod.rs | 6 + .../src/{worker.rs => worker/mod.rs} | 0 70 files changed, 220 insertions(+), 2724 deletions(-) delete mode 100644 crates/mvp-system/specs/MVP_SYSTEM_MODULE_BOUNDARY_SPEC.md delete mode 100644 crates/mvp-system/specs/mvp_chat.md delete mode 100644 crates/mvp-system/specs/orchestrator.md delete mode 100644 crates/mvp-system/src/actors/mod.rs create mode 100644 crates/mvp-system/src/chat/config.rs rename crates/mvp-system/src/{chat.rs => chat/mod.rs} (72%) rename crates/mvp-system/src/{ => chat}/node_image.rs (99%) delete mode 100644 crates/mvp-system/src/node.rs rename crates/mvp-system/src/{actors/node_agent.rs => node/actor.rs} (99%) rename crates/mvp-system/src/{node_boot_lifecycle.rs => node/boot_lifecycle.rs} (100%) rename crates/mvp-system/src/{edge_establisher.rs => node/edge_lifecycle.rs} (100%) create mode 100644 crates/mvp-system/src/node/mod.rs delete mode 100644 crates/mvp-system/src/node_data.rs rename crates/mvp-system/src/{arena_manager.rs => node_data/arena.rs} (100%) rename crates/mvp-system/src/{tx_rx_edge_actor.rs => node_data/edge_actor.rs} (100%) rename crates/mvp-system/src/{gpu_worker_ingress_parser.rs => node_data/ingress.rs} (100%) create mode 100644 crates/mvp-system/src/node_data/mod.rs delete mode 100644 crates/mvp-system/src/observability.rs rename crates/mvp-system/src/{benchmark_observability.rs => observability/benchmark.rs} (100%) rename crates/mvp-system/src/{ => observability}/dashboard_view.rs (99%) rename crates/mvp-system/src/{observability_surface.rs => observability/lifecycle.rs} (100%) create mode 100644 crates/mvp-system/src/observability/mod.rs rename crates/mvp-system/src/{bootstrap_datastream.rs => observability/provisioning_logs.rs} (100%) rename crates/mvp-system/src/{ => observability}/telemetry.rs (96%) rename crates/mvp-system/src/{actors/orchestrator.rs => orchestration/actor.rs} (99%) rename crates/mvp-system/src/{ => orchestration}/config.rs (81%) rename crates/mvp-system/src/{ => orchestration}/engine_builder/engine.rs (100%) rename crates/mvp-system/src/{ => orchestration}/engine_builder/error.rs (100%) rename crates/mvp-system/src/{ => orchestration}/engine_builder/events.rs (100%) rename crates/mvp-system/src/{ => orchestration}/engine_builder/launcher.rs (100%) rename crates/mvp-system/src/{ => orchestration}/engine_builder/mod.rs (100%) rename crates/mvp-system/src/{ => orchestration}/engine_builder/model.rs (100%) rename crates/mvp-system/src/{ => orchestration}/engine_builder/node_image.rs (100%) rename crates/mvp-system/src/{ => orchestration}/engine_builder/planner.rs (100%) rename crates/mvp-system/src/{ => orchestration}/engine_builder/pool.rs (100%) rename crates/mvp-system/src/{ => orchestration}/engine_builder/roles.rs (100%) rename crates/mvp-system/src/{ => orchestration}/engine_builder/runtime_stack.rs (97%) rename crates/mvp-system/src/{ => orchestration}/engine_builder/workload.rs (100%) rename crates/mvp-system/src/{orchestration.rs => orchestration/mod.rs} (86%) create mode 100644 crates/mvp-system/src/orchestration/provider_adapters/vastai/config.rs rename crates/mvp-system/src/orchestration/provider_adapters/{vastai.rs => vastai/mod.rs} (99%) rename crates/mvp-system/src/{ => orchestration}/resource_inventory.rs (100%) rename crates/mvp-system/src/{prompt.rs => prompt/mod.rs} (60%) rename crates/mvp-system/src/{prompt_rpc.rs => prompt/rpc.rs} (98%) rename crates/mvp-system/src/{actors/stage_controller.rs => staging/actor.rs} (100%) rename crates/mvp-system/src/{staging.rs => staging/mod.rs} (79%) delete mode 100644 crates/mvp-system/src/transport.rs create mode 100644 crates/mvp-system/src/transport/codec_registry.rs rename crates/mvp-system/src/{ => transport}/driver_pumps.rs (100%) rename crates/mvp-system/src/{ => transport}/endpoint_advertisement.rs (100%) rename crates/mvp-system/src/{actors/codec.rs => transport/json_codec.rs} (100%) create mode 100644 crates/mvp-system/src/transport/mod.rs rename crates/mvp-system/src/{worker.rs => worker/mod.rs} (100%) diff --git a/crates/mvp-system/specs/MVP_SYSTEM_MODULE_BOUNDARY_SPEC.md b/crates/mvp-system/specs/MVP_SYSTEM_MODULE_BOUNDARY_SPEC.md deleted file mode 100644 index 24e3f11..0000000 --- a/crates/mvp-system/specs/MVP_SYSTEM_MODULE_BOUNDARY_SPEC.md +++ /dev/null @@ -1,680 +0,0 @@ -# MVP System Module Boundary Specification - -**Status:** draft module-boundary specification. - -This document defines the intended behavioral modules for the MVP GPU pipeline -system and the reusable crates that should sit underneath it. It describes module -ownership in terms of behavior and contracts, not file layout. - -The MVP system is an application layer on top of swactor. It coordinates trusted -GPU workers for one linear model pipeline. General runtime pieces should live in -reusable crates when their contracts do not depend on GGUF, tinygrad, prompt -execution, or MVP-specific run policy. - ---- - -## 1. Purpose - -The module split has three goals: - -1. Separate reusable runtime infrastructure from MVP-specific GPU orchestration. -2. Give each module one durable ownership boundary and one black-box test surface. -3. Keep binaries as composition shells rather than homes for reusable behavior. - -A module boundary is correct when its tests can describe behavior without knowing -how a complete MVP run is wired together. - ---- - -## 2. Layering Principles - -The system is divided into two layers: - -```text -reusable crates - -> MVP system modules - -> binaries / operator entrypoints -``` - -Reusable crates own behavior that can be used outside the MVP GPU pipeline. MVP -modules own behavior that exists because this system runs staged GPU inference. -Binaries own process startup, command-line parsing, environment wiring, and -composition only. - -Rules: - -- Payload bytes remain opaque below MVP-specific worker/staging code. -- Provider leases do not know about model prompts or tensor shapes. -- Transport and data-plane modules do not know about GGUF, tinygrad, sampling, or - prompt policy. -- Orchestration owns run authority, but not local byte movement or GPU execution. -- Staging owns stage-local control and weight lifetimes, but not global run - policy. -- Worker owns GPU worker process control and device-facing protocol, but not - provider provisioning or global planning. -- Observability records behavior; it must not become a second control path. - ---- - -## 3. Reusable Crates - -### 3.1 data-plane - -The data-plane crate owns local payload memory and byte movement primitives that -are reusable outside the MVP GPU pipeline. - -It provides arena-backed allocation, ring layout descriptions, object record -framing, cursor/wake contracts, and local ingress/egress movement between process -boundaries. It does not know about GGUF, stages, prompts, provisioning providers, -or model execution. - -Responsibilities: - -- create and manage arena-backed memory regions -- lease and release ring ranges -- describe ring layouts without process-local pointers -- define object record headers and sequence contracts -- expose local producer/consumer ring operations -- enforce quiescence before arena reuse -- validate object structure, extent, alignment, and sequence policy -- provide local ingress and egress byte movement contracts - -Non-responsibilities: - -- model planning -- GPU execution -- provider provisioning -- prompt handling -- network peer discovery -- dashboard rendering -- interpreting tensor values or token content - -Test surface: - -- arena lease ordering and non-overlap -- ring cursor/wake behavior -- object header validation -- ingress/egress complete-object and partial-ring behavior -- quiescence before release -- sequence violation rejection - -### 3.2 provisioning - -The provisioning crate owns provider-neutral node lease lifecycle. - -It models desired nodes, provider leases, boot sessions, readiness probes, -destroy handles, and provider observations. Provider adapters may use local -processes, local containers, SSH sessions, or external lease APIs, but the core -lifecycle is provider-neutral. - -Responsibilities: - -- describe desired node shape and boot requirements -- acquire and track provider leases -- start boot sessions for leased nodes -- surface provider logs and lifecycle observations -- expose destroy handles for cleanup -- classify lease, boot, and teardown outcomes -- support deterministic mock providers for tests - -Non-responsibilities: - -- GPU stage assignment -- model weight planning -- prompt serving -- arena/ring allocation -- transport stream pumping -- provider-specific business logic beyond adapter boundaries - -Test surface: - -- lease acquisition success/failure -- boot session success/failure -- cleanup after partial acquisition -- retry and cancellation behavior -- provider adapter command/request construction -- destroy handle idempotence - -### 3.3 process - -The process crate owns supervised local process lifecycle. - -It starts processes, forwards commands, captures output, records exits, and -provides lifecycle observability. It does not own the meaning of a particular -child protocol. - -MVP worker JSON, tinygrad commands, and GPU worker states belong above this -crate. - -### 3.4 transport, distribution, and iroh-driver - -Transport crates own peer identity, message encoding, cluster membership, -routing, and concrete iroh/QUIC behavior. - -Responsibilities are split by abstraction: - -- transport: peer identity, codecs, envelope shapes, transport traits -- distribution: actor routing, node directory, membership, SWIM, route claims -- iroh-driver: concrete iroh endpoint, QUIC streams, relay/direct connectivity, - edge transport, datastream transport - -These crates do not know about stage ranges, GGUF, prompt policy, or worker -execution. - -### 3.5 datastream and dashboard - -The datastream crate owns observable frame transport, channel cataloging, -producer handles, ingest, storage, and read-side projections. - -The dashboard crate owns read-only presentation of datastream state. - -MVP-specific event names and payload vocabularies belong in the MVP -observability module, but frame transport and storage are reusable. - ---- - -## 4. MVP System Modules - -### 4.1 orchestration - -The orchestration module owns run authority for one MVP pipeline execution. - -It selects the intended node pool, builds the run plan, assigns stages and edges, -drives provisioning, observes readiness, injects prompts, consumes output tokens, -records terminal outcome, and initiates teardown. - -Responsibilities: - -- build and validate a run plan -- assign stage indices, layer ranges, edge ids, and object specs -- coordinate node availability and pool readiness -- dispatch stage provisioning -- enforce the global readiness barrier -- submit prompt work after readiness -- apply sampling and stop policy at the run boundary -- record completed, faulted, or operator-stopped outcomes -- initiate teardown after terminal outcome - -Non-responsibilities: - -- local ring allocation -- raw byte copying -- QUIC stream pumping -- GPU worker process control -- weight tensor loading internals -- provider-specific lease implementation -- dashboard rendering - -Test surface: - -- run plan validation -- layer and edge assignment invariants -- readiness gate behavior -- provisioning dispatch behavior -- prompt injection only after readiness -- sequence-ordered output consumption -- terminal outcome and teardown commands -- fault classification at the run boundary - -### 4.2 node - -The node module owns node-local runtime admission and composition state. - -A node is a trusted participant in a run. It owns local boot state, local actor -addresses, node availability, and the binding between local modules needed to -serve a provisioned stage. It does not decide global topology. - -Responsibilities: - -- represent node boot and node availability -- reject run work before local availability -- host node-local actors and service endpoints -- accept provisioned work from the authorized orchestrator -- route local control events between staging, worker, data-plane, and transport -- report node-local lifecycle and fault events - -Non-responsibilities: - -- global planning -- provider lease acquisition -- tensor interpretation -- prompt policy -- reusable arena implementation -- reusable process supervision - -Test surface: - -- boot state progression -- rejection before availability -- authorized provisioning admission -- local fault fanout -- node shutdown behavior - -### 4.3 staging - -The staging module owns stage-local control for assigned model ranges and raw -weight tensor lifetimes. - -A stage receives a provisioned role, materializes or locates the assigned weight -artifact, loads or binds the assigned layer range, validates inbound and outbound -edge readiness, and admits exactly one execution step at a time. - -Responsibilities: - -- validate stage provisioning -- own assigned stage index and layer range -- plan and track stage-local weight materialization -- load or bind assigned weights before readiness -- report stage readiness only after worker, weights, and edges are ready -- enforce per-stage sequence ordering -- issue worker execution commands for loaded input objects -- surface stage faults with stable reasons -- participate in teardown and weight/device release - -Non-responsibilities: - -- global run planning -- node leasing -- prompt tokenization -- raw ring cursor manipulation -- network transport -- provider bootstrap - -Test surface: - -- stage provisioning validation -- weight lifecycle success/failure -- readiness only after all local dependencies -- single active execution step -- sequence violation faulting -- teardown after ready, executing, and faulted states - -### 4.4 node_data - -The node_data module is the MVP-facing adapter over reusable data-plane -contracts. - -It connects node-local arena/ring/object movement to MVP edge and worker use. It -may define MVP object specs and helper conversions, but the raw arena/ring -implementation remains reusable. - -Responsibilities: - -- bind MVP object specs to data-plane object records -- adapt local rings to worker ingress and egress behavior -- expose node-local object loaded/produced events -- preserve sequence and extent guarantees at the node boundary -- coordinate local quiescence requests during teardown - -Non-responsibilities: - -- allocation algorithms that belong in the reusable data-plane crate -- network membership -- provider leases -- model planning -- prompt stop policy - -Test surface: - -- MVP object spec conversion -- ingress object readiness after full payload -- egress object production with correct headers -- sequence propagation through local movement -- teardown quiescence handoff - -### 4.5 transport - -The transport module is the MVP-facing adapter over swactor distribution and iroh -transport crates. - -It establishes the network-facing edge transport required by a run. It uses -cluster membership, route ownership, iroh endpoints, and persistent edge streams, -but it does not interpret payload bytes. - -Responsibilities: - -- bind run edge ids to concrete send/receive transport endpoints -- open and accept persistent edge streams -- bridge network stream lifecycle into node/stage edge lifecycle -- surface stream readiness and stream faults -- preserve ordering guarantees provided by the underlying stream - -Non-responsibilities: - -- arena allocation -- object header interpretation -- GPU execution -- provider leasing -- prompt policy - -Test surface: - -- edge preamble behavior -- send/receive establishment ordering -- stream-arrives-before-spec and spec-before-stream cases -- stream fault propagation -- connection reuse policy where applicable - -### 4.6 worker - -The worker module owns GPU worker process control and device-facing protocol. - -It treats the worker process as a supervised, command-driven participant. It -validates worker generation, installed rings, device handles, command admission, -and process lifecycle. It does not own provider leases or global run planning. - -Responsibilities: - -- start and initialize the GPU worker process -- maintain worker generation -- install and uninstall worker-visible rings -- configure stage role execution -- issue explicit execution commands -- route worker events to node and staging control -- classify worker fatal, step, object, and ring failures -- release device handles according to protocol - -Non-responsibilities: - -- local process spawning mechanics below the generic process crate -- run-level terminal outcome -- provider boot -- global stage assignment -- prompt session UX -- dashboard presentation - -Test surface: - -- initialization success/failure -- generation invalidation after restart -- command rejection before readiness -- install/uninstall ring behavior -- execution command admission -- worker crash fault fanout -- device handle release behavior - -### 4.7 prompt - -The prompt module owns the operator-to-run prompt protocol and prompt result -contract. - -It accepts prompt submissions, assigns request identity, sends work to the run -authority, receives token/text progress, and reports terminal prompt outcomes. -It does not own model planning or worker execution internals. - -Responsibilities: - -- define prompt request and prompt event protocol -- validate prompt request shape -- expose prompt progress and terminal events -- preserve request identity across async execution -- carry token/text output without becoming a transport hot path - -Non-responsibilities: - -- stdin UX -- provider leasing -- GPU sampling implementation internals -- raw ring movement -- global run lifecycle outside prompt admission/result - -Test surface: - -- request serialization and parsing -- terminal event detection -- progress event ordering -- malformed request rejection -- connection/session close behavior - -### 4.8 observability - -The observability module owns the MVP event vocabulary and emission helpers. - -It defines stable event identities and records needed to prove lifecycle behavior. -It uses the reusable datastream crate for frame transport and storage. It must -not participate in control decisions except through ordinary module APIs that -observe recorded events. - -Responsibilities: - -- define MVP lifecycle event names and payload shapes -- register MVP datastream channels -- emit structured lifecycle, progress, and fault records -- archive events for benchmark and contract evidence -- bridge provider/bootstrap logs into the event stream -- provide dashboard views over MVP events when enabled - -Non-responsibilities: - -- actor routing -- provider lease decisions -- run state mutation -- retry policy -- prompt response generation - -Test surface: - -- stable event field presence -- channel registration -- archive ordering and gap reporting -- benchmark envelope construction -- log-to-event bridge behavior - -### 4.9 chat - -The chat module owns the MVP operator-facing wrapper. - -It configures and launches an MVP run, prepares required runtime artifacts, -starts or embeds orchestration according to the selected mode, opens the prompt -session, and presents prompt results to the operator. - -Responsibilities: - -- parse chat command-line and chat-specific configuration -- prepare image/runtime artifacts for selected provider mode -- obtain explicit operator approval for rented-node runs when required -- start the orchestration layer in the supported wrapper mode -- run the prompt session loop -- emit chat lifecycle and benchmark events -- shut down the run on operator interrupt or session end - -Non-responsibilities: - -- standalone orchestration design -- provider-neutral lease lifecycle internals -- data-plane implementation -- worker protocol implementation -- model shard planning internals - -Test surface: - -- config and CLI resolution -- provider selection conflicts -- cached model selection -- prompt session behavior -- interrupt handling -- runtime preparation request construction -- operator approval behavior - ---- - -## 5. Dependency Direction - -Allowed dependency direction: - -```text -reusable crates - data-plane - provisioning - process - transport / distribution / iroh-driver - datastream / dashboard - -MVP modules - orchestration - node - staging - node_data - transport - worker - prompt - observability - chat - -binaries - mvp-chat - mvp-worker-node -``` - -Rules: - -- reusable crates must not depend on MVP modules -- chat may depend on orchestration, prompt, deployment behavior, and - observability -- orchestration may depend on staging contracts, node contracts, provider - contracts, prompt, transport, and observability -- staging may depend on worker, node_data, and GGUF/weight contracts -- worker may depend on process and node_data contracts -- node_data may depend on data-plane, not on orchestration or chat -- transport may depend on distribution and iroh-driver, not on staging or prompt -- observability may define MVP event vocabulary but must not call back into - runtime control paths - ---- - -## 6. Final Repository Map - -The final repository shape should keep reusable runtime crates outside -`mvp-system` and keep MVP binaries as thin composition roots. - -```text -crates/ - data-plane/ - arena, rings, object records, local byte movement - - provisioning/ - provider-neutral leases, boot sessions, destroy handles - - process/ - supervised local process lifecycle - - transport/ - peer identity, codecs, transport traits - - distribution/ - node directory, membership, routing, SWIM - - iroh-driver/ - iroh endpoint, QUIC streams, edge/datastream transport - - datastream/ - frame transport, catalog, ingest, archive, views - - dashboard/ - read-only datastream presentation - - mvp-system/ - orchestration/ - node/ - staging/ - node_data/ - transport/ - worker/ - prompt/ - observability/ - chat/ - - src/bin/mvp_chat.rs - src/bin/worker_node.rs -``` - ---- - -## 7. Binary Boundaries - -Binaries are composition roots. - -The chat binary owns operator entry and chat process lifetime. It should call the -chat module and avoid carrying reusable runtime logic. - -The worker-node binary owns node process entry, environment extraction, signal -handling, and assembly of node-local modules. It should not define reusable -stage, worker, data-plane, transport, or observability behavior. - -Binaries may contain: - -- `main` -- argument parsing glue -- environment extraction glue -- signal/shutdown wiring -- module assembly -- top-level error rendering - -Binaries should not contain: - -- protocol definitions -- FSM implementations -- provider-neutral lifecycle rules -- reusable object/ring parsing -- worker command/event semantics -- datastream archive logic - ---- - -## 8. Behavioral Test Surfaces - -Each module must expose a black-box contract surface. Tests should assert -observable behavior, not private structure. - -Preferred test subjects: - -- commands emitted by an FSM -- lifecycle events -- stable fault reasons -- accepted/rejected requests -- sequence and readiness transitions -- teardown/quiescence outcomes -- serialized protocol records - -Avoid tests that only restate implementation steps. - -Module test boundaries: - -```text -data-plane arena/ring/object movement contracts -provisioning lease/boot/destroy contracts -orchestration run authority and readiness contracts -node node admission and local routing contracts -staging stage readiness, weights, sequence, teardown contracts -node_data MVP object/ring adapter contracts -transport edge stream establishment/fault contracts -worker worker process/control/generation contracts -prompt request/event/session contracts -observability event vocabulary/archive contracts -chat operator wrapper/config/session contracts -``` - ---- - -## 9. Migration Constraints - -The module migration must preserve behavior while boundaries move. - -Constraints: - -- no token/activation algorithm changes during modularization -- no change to stage sequence semantics during code movement -- no change to run readiness requirements unless specified separately -- no change to provider behavior while extracting provider modules -- no new public duplicate type systems -- no long-lived compatibility shims after callsites migrate -- old paths may re-export during a short migration step, but final public surface - should use module-owned names - -A move is complete only when: - -1. the module has a clear public contract -2. duplicate definitions are removed or re-exported from one owner -3. behavior tests live at the module boundary -4. binaries only compose the module -5. the old public path is deleted or intentionally retained as the owner diff --git a/crates/mvp-system/specs/mvp_chat.md b/crates/mvp-system/specs/mvp_chat.md deleted file mode 100644 index 7eefd3a..0000000 --- a/crates/mvp-system/specs/mvp_chat.md +++ /dev/null @@ -1,798 +0,0 @@ -# MVP Chat Wrapper Fixed Specification - -**Status:** draft target behavior for `mvp-chat`. - -This document describes the intended public contract. Implementation details that -remain in the source but were marked out of scope are not part of this fixed -contract. - ---- - -## 1. Purpose - -`mvp-chat` is the user-facing process that starts the `mvp-chat` library runtime -and attaches an interactive prompt session to it. - -The wrapper is responsible for: - -- accepting the approved public inputs; -- resolving provider and runtime launch configuration; -- preparing required local runtime artifacts through Cargo unless rebuilds are - explicitly skipped; -- starting the library runtime and its orchestrator, prompt, and datastream leaves; -- waiting until the runtime can accept prompt requests; -- running the interactive prompt loop; -- notifying the runtime to shut down on normal exit or interruption; -- reporting errors clearly to the user. - -`mvp-chat` is not responsible for: - -- model inference quality; -- worker internals; -- node-image construction internals; -- orchestrator argument naming; -- provider API details beyond the inputs needed to request a provider-backed - runtime; -- dashboard rendering; -- non-Linux behavior. - ---- - -## 2. Supported Platform - -This specification covers Linux only. - -Linux signal handling, managed component and process-leaf lifecycle, Cargo -artifact discovery, and runtime shutdown semantics are the only supported -platform behavior. Non-Linux behavior is out of scope until explicitly specified. - ---- - -## 3. Public Input Surface - -`mvp-chat` accepts inputs only from the surfaces listed in this section. -Commented-out or implementation-only inputs from the earlier draft are pruned -from the public contract. - -### 3.1 Process Arguments - -Provider selectors: - -- `--process` -- `--docker` -- `--vastai` - -General flags: - -- `--config ` -- `--yes` -- `-y` -- `--pipeline-stages ` -- `--dump-logs` -- `--dump-logs=` -- `--cached-model` -- `--cached-model=` -- `--skip-rebuild` -- `--gpu` -- `--relay-mode ` -- `--relay-url ` -- `--endpoint-addr-mask ` - -Any process argument that is not one of the listed flags or a required or -attached value for one of those flags is a configuration error. - -`--process`, `--docker`, and `--vastai` are mutually exclusive. Supplying more -than one provider selector is a configuration error. - -If no provider selector is supplied, the provider is `process`. - -`--pipeline-stages ` accepts a positive integer. Zero and invalid values -are configuration errors. - -`--relay-mode ` accepts `default` or `disabled`. - -`--endpoint-addr-mask ` accepts `full` or `relay-only`. `relay-only` -requires a relay URL from `--relay-url`, `[relay].url`, or `[vastai].relay_url`. - -`--dump-logs` writes the consolidated log stream to the default file -`mvp-chat.log` in the current working directory. - -`--dump-logs=` writes the same stream to ``. The `` value is -the literal string after `=`, may start with `-`, and may be relative or -absolute. Relative paths are resolved relative to the current working directory. -`--dump-logs=` is a configuration error. - -`--dump-logs ` is not accepted. Without `--dump-logs`, logs are not stored -in a file. - -`--cached-model` enables cached-model use and discovers a cached model from -`.model-cache/`. - -`--cached-model=` uses `` as the cached model file. The `` -value is the literal string after `=`, may start with `-`, and may be relative -or absolute. Relative paths are resolved relative to the current working -directory. `--cached-model=` is a configuration error. - -`--cached-model ` is not accepted. Without `--cached-model`, cached-model -use is disabled. - -`--skip-rebuild` prevents `mvp-chat` from invoking Cargo builds. If a required -runtime artifact is unavailable while rebuilds are skipped, preparation fails -with a clear error. - -### 3.2 Environment Variables - -The public configuration environment surface is limited to secret material. - -Accepted environment variable: - -- `VAST_API_KEY` - -`VAST_API_KEY` supplies the Vast.ai API key when the selected provider is -`vastai`. The value is trimmed before validation; an unset, empty, or -whitespace-only value is treated as missing. - -No other environment variable is part of the public `mvp-chat` configuration -contract. Normal inherited process environment, such as the environment used by -Cargo or child processes, is ordinary OS execution context rather than -`mvp-chat` configuration. - -### 3.3 Configuration File - -`mvp-chat` reads configuration from a TOML file. - -The config path is: - -- `--config `, when supplied; -- otherwise `.config/config.toml` relative to the current working directory. - -A supplied `--config ` is required to be a readable file and parse as -TOML. The default `.config/config.toml` is optional and is read only when it -exists as a file. If the default config file exists but cannot be read or parsed, -configuration fails; if the default path is absent or not a file, built-in -defaults are used. - -The fixed spec accepts only active behavior fields. Unused schema fields from the -earlier draft are pruned. Unknown top-level TOML tables and unknown fields inside -accepted tables are configuration errors. - -Accepted provider field: - -- `[provider].kind` - -`[provider].kind` is trimmed before validation. After trimming, accepted values -are case-sensitive and exactly `process`, `docker`, or `vastai`. - -Accepted runtime fields: - -- `[runtime].pipeline_stages` -- `[runtime].max_tokens` - -`[runtime].max_tokens` sets the maximum number of tokens requested for each -prompt submission. A value of `0` implies no specified limit. - -Accepted observability fields: - -- `[observability].dump_logs` -- `[observability].dump_log_path` - -Accepted image fields: - -- `[image].node` -- `[image].tag` - -`[image].node` names the desired worker node image. - -For provider `docker`, `[image].node` may name a local or remote image. - -For provider `vastai`, `[image].node` must name a remote registry image that the -provider can pull. - -`[image].tag` may provide an additional human-selected tag or alias for an image -prepared by `mvp-chat`. It does not replace the resolved image reference used for -freshness or content identity. - -Accepted Vast.ai fields: - -- `[vastai].relay_url` -- `[vastai].bootstrap_command` -- `[vastai].gpu_name` -- `[vastai].min_gpu_ram_mb` -- `[vastai].min_down_mbps` -- `[vastai].min_up_mbps` -- `[vastai].min_reliability` -- `[vastai].require_verified` -- `[vastai].disk_gb` -- `[vastai].onstart` -- `[vastai].ssh_identity` - -`[vastai].ssh_identity` is a filesystem path to an SSH private-key identity file -used for provider bootstrap access. It is configuration, not a secret-value -environment variable. - -### 3.4 Standard Input - -Accepted standard input: - -- interactive prompt lines; -- EOF or input disconnection; -- standard-input read error during prompt input; -- Vast.ai rental approval response when approval is required. - -Prompt lines drive the prompt loop. EOF, input disconnection, and standard-input -read errors during prompt input end the prompt loop cleanly and trigger normal -runtime cleanup. There are no prompt text commands for exiting. - -While waiting for prompt input, `mvp-chat` must still respond to EOF, input -disconnection, standard-input read errors, `SIGINT`, and `SIGTERM`. The exact -input-read mechanism is an implementation detail. - -### 3.5 Signals - -Accepted Linux signals: - -- `SIGINT` -- `SIGTERM` - -Both request controlled shutdown. - -### 3.6 Filesystem Inputs - -Filesystem inputs: - -- current working directory; -- config file selected by Section 3.3; -- `.model-cache/` when `--cached-model` is supplied; -- cached model file path when `--cached-model=` is supplied; -- Cargo workspace files needed by Cargo to build or locate runtime artifacts; -- Cargo target artifacts for the orchestrator and worker; -- optional Vast.ai SSH identity file path from config; -- optional dump-log output path parent directories. - -Additional filesystem inputs for image preparation: - -- node image Dockerfile; -- node image build context; -- worker binary artifact included in the image; -- source files used to determine image freshness; -- local Docker image metadata. - -The current working directory is the root for relative paths. - -When `--cached-model` is supplied, `mvp-chat` reads the direct files in -`.model-cache/`, filters for model files accepted by the runtime, sorts the -remaining files alphabetically by filename, and selects the first file. Failure -to read `.model-cache/`, read a direct directory entry, or stat a direct entry is -a preparation error. Direct entries that can be statted but do not match the -cached-model predicate are ignored. If no usable cached model is present, -preparation fails with a clear error. - -When `--cached-model=` is supplied, `mvp-chat` validates that the path is a -usable cached model file. A usable cached model path must resolve to a regular -file whose extension is `.gguf`, matched case-insensitively. If it is missing, -not a regular file, or not accepted by this predicate, preparation fails with a -clear error. Accepted cached-model paths are canonicalized before being included -in the runtime launch request. - -### 3.7 Network and Runtime Inputs - -Runtime inputs: - -- prompt engine readiness outcome; -- prompt engine `PromptEvent` stream records; -- orchestrator and component progress events written to the local datastream. - -Additional provider/image inputs: - -- Docker daemon responses when checking local image availability; -- registry responses when checking remote image availability; -- registry responses when pushing images for remote providers. - -Additional relay inputs: - -- relay mode from `--relay-mode`, `[relay].mode`, or `MVP_IROH_RELAY_MODE`; -- relay URL from `--relay-url`, `[relay].url`, `[vastai].relay_url`, or relay - environment fallbacks; -- endpoint address mask from `--endpoint-addr-mask`, `[relay].endpoint_addr_mask`, - or `MVP_IROH_ENDPOINT_ADDR_MASK`. - -When the endpoint address mask is `relay-only`, orchestrator and worker -advertisements must preserve relay URLs and strip direct socket addresses before -passing endpoints across provider/runtime boundaries. A missing relay URL is a -configuration or startup error. - -Prompt engine stream records are runtime-local prompt events: - -- text delta; -- request completion; -- request fault. - -Detailed progress payload schemas are not specified here. This spec only -requires that `mvp-chat` receive enough progress information to present the -user-facing progress states defined in Section 7. - -### 3.8 Managed Component Inputs - -Managed component inputs observed by `mvp-chat`: - -- orchestrator leaf start success or failure; -- orchestrator leaf readiness result; -- orchestrator leaf fault or exit before readiness; -- prompt engine leaf readiness result; -- prompt engine leaf fault before readiness; -- failure to request or wait for managed-component shutdown. - -The OS process APIs, process actors, and actor-runtime notifications used to -observe these states are implementation details. The observable contract is the -resulting success, failure, readiness, fault, or controlled shutdown. - ---- - -## 4. Provider Selection - -`mvp-chat` has no public runtime-profile concept. The public provider choices -are: - -- `process` -- `docker` -- `vastai` - -Provider resolution order: - -1. CLI provider selector. -2. TOML `[provider].kind`. -3. default `process`. - -The accepted provider values are exactly: - -- `process` -- `docker` -- `vastai` - -Compatibility aliases may exist in implementation, but they are not part of the -fixed public contract. - -If `vastai` is selected, required Vast.ai configuration must be present before -offer preview, approval, or launch. Missing required Vast.ai configuration is a -configuration error. - ---- - -## 5. Configuration Resolution - -Configuration is resolved from: - -- process arguments; -- TOML configuration; -- approved secret environment variables; -- fixed defaults. - -Process arguments override TOML where both define the same behavior. - -The only approved environment override is `VAST_API_KEY` for the Vast.ai API key. -Other configuration must come from process arguments, TOML, fixed defaults, or -filesystem discovery. - -Default values: - -- provider: `process`; -- pipeline stages: `1`; -- max tokens: `0`; -- dump logs: disabled; -- cached model: disabled unless `--cached-model` is supplied; -- rebuild: enabled unless `--skip-rebuild` is supplied. - -Invalid values must fail before runtime preparation begins. - -For provider `process`, no node image is required. - -For provider `docker`, an image reference is required. It may be local or remote. - -For provider `vastai`, an image reference is required and must be a remote -registry image. - -If a provider requires an image and no valid image reference is configured, -configuration fails before runtime preparation. - ---- - -## 6. Runtime and Image Artifact Preparation - -### 6.1 Cargo Runtime Artifacts - -`mvp-chat` obtains the orchestrator and worker artifacts through Cargo. - -The wrapper must not infer the orchestrator or worker path by changing the file -name of the current executable. - -The current working directory is the artifact root and must be available. The -default orchestrator artifact path is `target/debug/mvp-orchestrator` under that -directory. The default worker artifact path is `target/debug/mvp-worker-node` -under that directory. No fallback artifact root is defined. - -Unless `--skip-rebuild` is supplied, `mvp-chat` may invoke Cargo to make required -artifacts available. - -Approved Cargo builds: - -- `cargo build --quiet -p mvp-system --bin mvp-orchestrator` -- `cargo build --quiet -p mvp-system --bin mvp-worker-node` - -When `--skip-rebuild` is supplied: - -- `mvp-chat` must not invoke Cargo builds; -- required Cargo artifacts must already be available; -- missing Cargo artifacts are preparation errors. - -Worker binary behavior mirrors orchestrator binary behavior: both are resolved -through Cargo artifacts, both honor `--skip-rebuild`, and both fail clearly when -required artifacts are unavailable. - -### 6.2 Node Image Preparation - -Node image preparation applies only to provider-backed runtimes: - -- `docker` -- `vastai` - -Provider `process` does not require a node image. - -For provider-backed runtimes, `mvp-chat` performs node image resolution before -launching the orchestrator. Node image resolution consumes: - -- the selected provider; -- `[image].node`; -- optional `[image].tag`; -- rebuild policy from `--skip-rebuild`; -- the worker binary artifact selected for this run; -- the approved node-image Dockerfile and build context; -- source files and metadata used to determine image freshness; -- Docker daemon observations for local images; -- registry observations for remote images. - -Node image resolution produces the resolved image reference included in the -orchestrator launch request. - -For provider `docker`, the resolved image must be runnable by the local Docker -daemon. The image may be local or remote. - -For provider `vastai`, the resolved image must be pullable by the remote -provider and must include a registry/repository namespace. Local-only image names -are invalid. - -When rebuilds are enabled, `mvp-chat` must determine whether the requested image -is already acceptable for the selected provider and current runtime inputs. If no -acceptable image is available, `mvp-chat` may build, tag, push, and validate an -image as required by the selected provider. - -An acceptable prepared image is one that: - -- is usable by the selected provider; -- was prepared from the approved node-image Dockerfile and build context; -- includes the selected worker binary artifact; -- is not stale with respect to the freshness inputs used by the - image-preparation contract; -- has any configured `[image].tag` alias applied when applicable. - -When `--skip-rebuild` is supplied, `mvp-chat` must not build, tag, or push -images. It must use only existing image artifacts and fail clearly if the -required image is missing, stale, unavailable, or unsuitable for the selected -provider. - -The exact freshness algorithm, metadata format, Docker commands, cache policy, -and registry authentication mechanics are owned by the node-image preparation -contract. - -### 6.3 Cached Models and Images - -Cached model selection is independent from node image preparation unless an -approved image-preparation contract explicitly says otherwise. - -By default, `mvp-chat` treats cached models as runtime inputs, not as image -contents. It must not silently bake cached models into prepared images. - ---- - -## 7. Progress, Logs, and Datastream - -Normal runtime logs are consolidated into one `mvp-chat` log stream. - -Without `--dump-logs`, the stream is not stored in a file. - -With `--dump-logs`, the stream is written to the default file defined in Section -3.1. - -With `--dump-logs=`, the stream is written to the specified path according -to the path parsing rules in Section 3.1. - -`mvp-chat` must not create hidden startup archive files as part of the public -contract. - -Progress observation uses the `mvp-chat` local datastream endpoint, not -archive-file polling. - -The runtime owns one local endpoint: - -- stream id: `StreamId::new(NodeId::new("mvp-chat"), Lifetime(run_id))`; -- label: `"mvp chat"`; -- origin: `StreamOrigin::Orchestrator` until a chat-specific origin exists. - -Required channels: - -- `mvp.chat.lifecycle`; -- `mvp.chat.runtime`; -- `mvp.chat.prompt`; -- `mvp.chat.component`. - -Components write through cloned `DatastreamProducer` handles or through local -adapters installed when a component leaf starts. The datastream task drains the -local endpoint and fans frames out to subscribers. Payload schemas remain owned -by the datastream/progress contract. - -The user-facing progress model must eventually define visible transitions for -runtime startup. Until that model is approved, this spec only fixes prompt-loop -output in Section 10 and keeps non-prompt progress output deferred. - ---- - -## 8. Vast.ai Behavior - -When provider is not `vastai`, Vast.ai config and approval are not used. - -When provider is `vastai`, required configuration must be present before any -offer preview or launch. - -Required Vast.ai inputs: - -- API key from `VAST_API_KEY`; -- relay URL from config; -- node image reference from config; -- bootstrap command when required by the provider contract. - -Optional Vast.ai selection inputs: - -- GPU name; -- minimum GPU RAM; -- minimum downlink bandwidth; -- minimum uplink bandwidth; -- minimum reliability; -- verified-host requirement; -- disk size; -- onstart command; -- SSH identity file path. - -If approval is required and `--yes` is not supplied, `mvp-chat` asks the user for -approval through the terminal. Only `y` and `yes`, after trimming and -case-folding, approve the rental. Any other answer declines. - -If `--yes` or `-y` is supplied, approval is accepted non-interactively after -required configuration is validated. - -If approval is required but standard input is not interactive, `mvp-chat` fails -unless `--yes` or `-y` is supplied. - ---- - -## 9. Orchestrator Launch and Shutdown - -Exact orchestrator argv is out of scope until the orchestrator launch contract is -specified. - -`mvp-chat` is responsible for handing the resolved runtime request to the -orchestrator leaf through the library runtime. The production process-backed leaf -owns binary resolution, `ProcessSpec` construction, and managed process actor -startup; successor in-process leaves start the orchestrator actor group directly. - -The semantic launch request must include, as applicable: - -- selected provider; -- resolved node image reference for provider-backed runtimes; -- pipeline stage count; -- cached model selection result; -- dump-log configuration; -- provider-specific runtime configuration; -- datastream producer or adapter wiring required by the orchestrator leaf. - -The orchestrator launch contract does not define prompt transport. Prompt work is -handled by the `mvp-chat` prompt engine actor/task through runtime-local -messages. - -The resolved node image reference is the image the orchestrator must use for the -provider-backed node. Exact argv or wire encoding remains owned by the -orchestrator launch contract. - -The wrapper starts the `mvp-chat` library runtime. The runtime starts the -orchestrator leaf, prompt engine leaf, datastream task, swactor runtime, and -control path. Production process-backed leaves are managed by process actors; -`mvp-chat` must not directly own `std::process::Child` for long-lived -components. - -On shutdown, `mvp-chat` must request shutdown through the runtime control path. -The shutdown mechanism for each managed component is owned by that component's -leaf contract. - -Shutdown must be idempotent from the user's perspective. Normal prompt exit, -input EOF, startup interruption, and signal interruption must not leave the -runtime running when `mvp-chat` can notify it. - ---- - -## 10. Prompt Loop - -The prompt loop accepts user prompt lines from standard input. - -For each cycle, `mvp-chat` must: - -- display a prompt marker; -- read one line of input; -- remove trailing whitespace from the input line before prompt handling, while - preserving leading whitespace; -- exit cleanly for EOF, input disconnection, or standard-input read error; -- ignore prompts that are empty after whitespace trimming; -- submit non-empty prompts to the prompt engine actor/task; -- display that decoding has started; -- stream response text as `PromptEvent` values arrive; -- return to the prompt marker after completion or prompt fault. - -Prompt requests carry: - -- request id; -- prompt text; -- max token limit resolved from `mvp-chat` configuration; -- reply target for the `PromptEvent` stream. - -Prompt responses are: - -- `PromptEvent::TextDelta`; -- `PromptEvent::Done`; -- `PromptEvent::Fault`. - -`mvp-chat` submits at most one prompt at a time to the prompt engine. It waits -for a terminal `Done` or `Fault` event before submitting the next prompt. The -prompt engine guarantees that response events for an active request arrive in -order on the reply target. - -The prompt-loop output states are: - -- waiting for prompt; -- prompt submitted; -- decoding; -- streaming response; -- request completed; -- request faulted; -- prompt loop exited. - -Prompt-loop user output goes to standard output unless it is an actual wrapper -error. Expected model faults are prompt-loop results, not wrapper diagnostics. - -A fixed transport or read timeout is not part of the contract. The implementation -must remain interruptible, but this spec does not require a timeout-based -mechanism. - ---- - -## 11. Public Output Surface - -### 11.1 Exit Codes - -Exit code `0` means clean completion or controlled interrupted shutdown. - -Exit code `1` means configuration failure, preparation failure, startup failure, -prompt engine failure, managed runtime failure, or another wrapper error. - -### 11.2 Standard Output - -Standard output is for expected user-facing behavior. - -Standard output includes: - -- prompt marker; -- decoding marker; -- response prefix; -- response text; -- prompt-loop completion formatting; -- expected prompt fault display; -- Vast.ai approval prompt when interactive approval is required. - -Non-prompt startup progress output is deferred until the progress event model is -approved. - -### 11.3 Standard Error - -Standard error is for actual wrapper errors and exceptional diagnostics. - -Standard error must not be used for ordinary status messages such as successful -provider selection, normal build status, normal cached-model selection, or normal -prompt-loop events. - -Errors must be clear enough for the user to identify the failed input or failed -runtime phase. - -Image preparation errors must be displayed clearly when image preparation fails. - -Image-preparation errors include: - -- missing required image reference; -- invalid image reference; -- required rebuild skipped; -- local image unavailable; -- remote image unavailable; -- image build failure; -- image tag failure; -- image push failure. - -### 11.4 Filesystem Outputs - -Filesystem outputs are limited to: - -- Cargo build artifacts when rebuilds are enabled; -- dump-log file when `--dump-logs` is supplied; -- local Docker image layers when image rebuilds are allowed; -- local Docker image tags or aliases when image rebuilds are allowed; -- image build cache entries when image rebuilds are allowed; -- provider/runtime artifacts owned by external contracts, if those contracts are - invoked. - -`mvp-chat` MUST NOT create unspecified filesystem outputs. - -### 11.5 Network Outputs - -Network-visible outputs: - -- `mvp-chat` datastream endpoint for dashboard/user observers when progress - observation is active. - -Additional network-visible outputs when preparing remote images: - -- registry manifest checks; -- image layer uploads; -- image manifest or tag pushes. - -Prompt submissions are runtime-local messages to the prompt engine actor/task; -they are not network-visible outputs. - -### 11.6 Managed Runtime Outputs - -Outputs to managed runtime components are limited to the approved orchestrator -leaf launch and shutdown contracts, prompt engine request messages, and -datastream frames. - -This spec does not define exact argv names, stdin control strings, private -orchestrator flags, or internal actor message encodings beyond the prompt request -and event shapes in Section 10. - ---- - -## 12. Error Handling - -Configuration errors must be detected before runtime preparation where possible. - -Preparation errors must identify the missing artifact, invalid file, failed Cargo -operation, or invalid provider configuration. - -Startup errors must identify the failed startup phase when progress information -is available. - -Unexpected prompt engine errors must identify whether request submission, -event-stream closure, prompt event handling, or component fault failed. - -Controlled shutdown is not an error. - -Errors are displayed clearly to the user and cause nonzero exit unless the error -occurs during a controlled shutdown path defined as successful by this spec. - ---- - -## 13. Out of Scope - -Out of scope for this document: - -- path display formatting as a standalone contract; -- exact orchestrator argv; -- detailed datastream payload schemas; -- non-Linux support; -- Dockerfile contents; -- base-image implementation details; -- registry authentication UX beyond clear preparation errors; -- image optimization policy; -- image garbage-collection policy; diff --git a/crates/mvp-system/specs/orchestrator.md b/crates/mvp-system/specs/orchestrator.md deleted file mode 100644 index 6eed2b5..0000000 --- a/crates/mvp-system/specs/orchestrator.md +++ /dev/null @@ -1,954 +0,0 @@ -# MVP Orchestrator Actor Specification - -**Status:** normative contract for the actor-only MVP orchestrator. - -This document defines the observable behavior of the MVP orchestrator as a Swactor actor or actor group. The orchestrator is not specified as an operating-system process, executable, command-line program, or owner of a Tokio/Iroh/Swactor engine. It runs inside any host that supplies a compatible Swactor runtime plus iroh-driver transport bridge. - ---- - -## 1. Purpose and Contract Boundary - -The orchestrator is the run authority for one MVP worker runtime. It turns an already-resolved launch request into actor commands, readiness decisions, prompt execution, lifecycle observations, and teardown decisions. - -The orchestrator is responsible for: - -- accepting a typed run request from its host or supervisor actor; -- building or accepting the execution shape for the run; -- requesting worker provisioning through actor-managed provider/provisioner leaves; -- driving readiness from actor reports plus engine route and membership facts; -- acknowledging worker runtime readiness; -- provisioning worker stages; -- waiting for weights and stage readiness; -- accepting prompt work through actor messages; -- dispatching direct or pipeline prompt execution through worker actors and token edges; -- emitting lifecycle, prompt, provisioning, readiness, and fault observations; -- initiating actor-based teardown for stages, token endpoints, and provider-owned workers. - -The orchestrator is not responsible for: - -- parsing CLI arguments, environment variables, or TOML files; -- owning an executable or binary launch contract; -- choosing or creating a Tokio runtime; -- creating or owning a Swactor runtime; -- creating or owning an `IrohDriver` endpoint; -- owning the actor scheduler, pump loop, or runtime shutdown; -- managing raw QUIC streams, ALPN negotiation, iroh connections, or Tokio tasks; -- supervising operating-system child processes directly; -- reading standard input or writing status to standard output/error; -- exposing prompt TCP RPC; -- implementing dashboard rendering; -- implementing provider marketplace behavior, worker internals, model execution, tokenizer quality, or GGUF parsing beyond the typed facts it receives. - -The orchestrator may run in the same process as a wrapper, worker supervisor, dashboard, or test harness. That process is the host. Host behavior is outside this orchestrator actor contract unless it is observed through the actor/datastream surfaces defined here. - ---- - -## 2. Engine and Host Contract - -The host supplies an actor-capable engine. The engine must provide: - -- a Swactor runtime with typed actor mailboxes; -- actor addresses (`ActorAddress`); -- local actor spawn/send/inbox semantics; -- registered codecs for MVP actor messages; -- an iroh-driver actor bridge for remote actor delivery when remote workers exist; -- a route view that can answer which node owns a remote actor address; -- a membership view that can answer whether a worker node is alive; -- datastream logical subscription and collection support when observability is enabled. - -A conforming engine must be able to perform this work: - -```text -Tokio handle --> IrohDriver::with_handle --> DistributionRuntimeStack --> register_mvp_actor_codecs --> enable_actor_bridge --> spawn orchestrator actor/group --> register local actor route --> pump engine work -``` - -The exact host API is not part of this specification. The observable requirement is that actor messages accepted by the runtime make progress according to Swactor delivery semantics and remote actor traffic is routed through the iroh-driver actor bridge when the destination is remote. - -The host owns engine progress. It may drive progress with a tick loop, a worker-thread runtime, or a wake-driven loop. A blocking host wait that prevents actor delivery, iroh ingress, iroh egress, datastream collection, membership updates, or route updates from progressing violates the orchestrator runtime model. - -The orchestrator must not depend on a particular host executable, test harness, wrapper, or process name. - ---- - -## 3. Actor Topology - -The orchestrator contract is defined at actor-group boundaries. An implementation may split the group differently, but the same observable messages, reports, lifecycle decisions, and ordering guarantees must hold. - -### 3.1 Required Actor Participants - -Required participants: - -- **Orchestrator actor/group** - - owns run-level state; - - accepts run observations, prompt submissions, shutdown requests, and snapshots; - - emits commands and lifecycle reports. - -- **Provisioner actor/group** - - owns provider-facing node lifecycle; - - starts/stops provider-owned worker leaves; - - converts provider/plugin/managed-process observations into actor reports and datastream records. - -- **Worker node agent actor** - - lives on each worker runtime; - - receives stage provisioning, readiness acknowledgements, prompt inference, tokenizer encode/decode, and stop messages; - - reports runtime readiness, weights, stage readiness, faults, and stop completion back to the orchestrator. - -- **Datastream publisher/collector actors or adapters** - - carry logical datastream subscription and frame transport; - - do not own lifecycle authority. - -- **Prompt reply actor or reply target** - - receives prompt events for an active request. - -- **Tokenizer reply actor or reply target** - - receives tokenizer encode/decode events in pipeline mode. - -### 3.2 Optional Actor Participants - -Optional participants: - -- dashboard sinks; -- frame archive sinks; -- managed process actors for local workers or helper processes; -- provider-specific actor leaves; -- mock/stub actor leaves for tests. - -Optional participants must not change orchestrator lifecycle authority. They may observe, mirror, or adapt behavior; they do not make readiness, prompt completion, or shutdown true by themselves. - -### 3.3 Actor Codecs - -The MVP actor codec registry must include the message families required by the active topology: - -```text -NodeAgentMsg / NodeAgentReport -OrchestratorMsg / OrchestratorReport -ProvisionerMsg / ProvisionerReport -DatastreamPublisherMsg -PromptEvent / TokenizerEvent or their actor-prompt successors -``` - -The module or source file where a codec is registered is not a public contract. The contract is that every actor message that may cross the iroh actor bridge has a registered codec and type tag. - ---- - -## 4. Inputs - -The orchestrator accepts typed actor inputs only. Configuration files, environment variables, process arguments, filesystem discovery, interactive input, OS signals, and TCP connections are host or adapter inputs. A host may translate those inputs into typed actor messages, but the translation is outside this orchestrator actor contract. - -### 4.1 Run Request - -A run starts with a typed run request delivered to the orchestrator actor/group. - -Required run request facts: - -```text -RunRequest { - run_id, - orchestrator_node_id, - provider_policy, - worker_image_or_artifact_reference, - worker_count_or_run_plan_input, - model_identity, - model_source, - tokenizer_source, - pipeline_stages, - default_max_tokens, - max_context, - relay_or_endpoint_facts_needed_by workers, - observability_policy, - provider_preparation_result, - prompt_policy, -} -``` - -The request must be typed before the orchestrator receives it. The orchestrator must not parse raw strings from CLI, TOML, or environment variables as part of this contract. - -If a provider requires secrets or external preparation, the host or provider actor supplies prepared typed facts. Secret values must not be emitted by orchestrator-owned datastream records or lifecycle reports. - -### 4.2 Run Plan Input - -The orchestrator may either: - -- receive a committed `RunPlan`; or -- receive locally inspectable model facts sufficient for a planner actor/component to produce a committed `RunPlan`. - -A committed plan includes: - -```text -RunPlan { - run_id, - orchestrator_node_id, - stage_count, - stage_refs, - layer_ranges, - token_in_edge, - activation_edges, - token_out_edge, - object_specs, - ring_specs, - tokenizer_source, - model_source, -} -``` - -Direct execution is represented as one stage. Pipeline execution is represented as two or more planned stages or any run whose prompt path requires token-in/token-out endpoints. - -The orchestrator must not accept a worker-generated topology. Workers may report boot/runtime facts; they do not assign stage indexes, layer ranges, edge ids, object specs, or consumer endpoints. - -### 4.3 Provisioning Reports - -The orchestrator receives provisioning reports through actors. Active report kinds: - -```text -ProvisionerReport::NodeLive { ... } -ProvisionerReport::NodeFailed { ... } -ProvisionerReport::LogLine { ... } -ProvisionerReport::NodesStopped { ... } -``` - -`LogLine` is observational. It must not make a node ready or failed by itself unless accompanied by a typed failure report. - -### 4.4 Worker Runtime Reports - -Workers report runtime readiness and stage state through actor messages. - -A runtime-ready report contains: - -```text -NodeRuntimeReady { - run_id, - node_id, - stage_index, - endpoint, - node_actor, - datastream_publisher, - readiness_id, -} -``` - -A runtime-ready acknowledgement report contains: - -```text -NodeRuntimeReadyAck { - run_id, - node_id, - stage_index, - readiness_id, -} -``` - -Weight/stage reports contain: - -```text -WeightsReady { run_id, node_id, stage_index } -StageReady { run_id, stage_index } -StageFault { run_id, stage_index, reason } -StageStopped { run_id, stage_index } -``` - -Reports with mismatched run id, node id, stage index, or readiness id must not advance the active run. - -### 4.5 Prompt Input - -Prompt input is an actor message, not TCP RPC. - -Prompt request shape: - -```text -SubmitPrompt { - request_id, - prompt_text, - max_tokens, - reply_to, -} -``` - -`reply_to` is the actor address that receives prompt events for this request. - -When `max_tokens` is absent or zero, the orchestrator does not impose an arbitrary generated-token cap. Generation may still stop on EOS, context/window exhaustion, worker fault, shutdown, or runtime/model limits. - -### 4.6 Shutdown Input - -Shutdown is an actor/control message. - -Required shutdown shape: - -```text -RequestShutdown { - run_id, - reason, - reply_to: optional, -} -``` - -Shutdown reason examples: - -```text -operator_requested -host_requested -prompt_session_closed -fatal_dependency -``` - -Text commands such as `stop`, `shutdown`, and `quit` are wrapper inputs only if a wrapper chooses to support them. They are not orchestrator actor inputs until translated into `RequestShutdown`. - -### 4.7 Membership and Route Observations - -The orchestrator may observe membership loss and route changes through host-provided actor messages or by querying engine-provided views. - -Required facts: - -```text -member_state(worker_swim_node_id) == Alive -route_owner(node_actor) == worker_swim_node_id -``` - -A membership loss for an active worker after readiness is a run fault unless the run is already tearing down. - ---- - -## 5. Outputs - -The orchestrator emits typed actor outputs and datastream observations. - -### 5.1 Command Reports - -Run commands are emitted as actor reports or sent directly to the responsible actor. Required command semantics: - -```text -ProvisionNodes -ProvisionStage -CreateTokenInEndpoint -CreateTokenOutEndpoint -RuntimeReadyAck -SubscribeDatastream -InferPrompt -EncodePrompt -DecodeTokens -InjectTokenObject -StopRun -TearDownTokenEndpoints -StopNodes -``` - -A successful actor send means the runtime accepted the message for routing. It does not prove the destination acted. Every command requiring acknowledgement must have an explicit acknowledgement or later lifecycle report. - -### 5.2 Lifecycle Reports - -Lifecycle reports include: - -```text -RunAccepted -RunRejected -RunPlanningStarted -RunPlanningReady -RunProvisioningStarted -RunReadinessStarted -RunReady -PromptAccepted -PromptCompleted -PromptFaulted -RunOperatorStopped -RunFaulted -RunTearingDown -RunTornDown -``` - -Lifecycle message names are normative at the actor boundary. Internal types may use different names as long as the required states remain observable. - -### 5.3 Prompt Events - -Prompt reply targets receive: - -```text -PromptEvent::TextDelta { request_id, text } -PromptEvent::Done { request_id, final_text, tokens_generated, elapsed_ms } -PromptEvent::Fault { request_id, error } -``` - -`TextDelta` is non-terminal. `Done` and `Fault` are terminal. A prompt request must receive exactly one terminal event unless the reply target disappears; if the reply target disappears, the orchestrator must cancel or fault the active prompt and continue teardown rules correctly. - -### 5.4 Datastream Output - -Datastream records are observational. They may describe lifecycle transitions, prompt progress, provisioning events, worker logs, provider logs, membership transitions, route checks, token-edge progress, dashboard frames, or frame archive records. - -Datastream records must not carry lifecycle authority. They must not be interpreted as commands. They must not make readiness, prompt completion, failure, or shutdown true. - -Required core observation channels are logical, not process-owned: - -```text -mvp.orch.bootstrap -mvp.orch.prompt -mvp.orch.lifecycle -mvp.orch.stage_route -mvp.swim.membership -mvp.provisioning.events -mvp.provisioning.logs.node.. -``` - -The orchestrator actor/group does not own stdout/stderr channels. Worker/provider stdout/stderr may be observed by provider or managed-process adapters and published as provisioning log records. - ---- - -## 6. Runtime Lifecycle - -The actor-only lifecycle is: - -```text -host starts engine --> host spawns orchestrator actor/group --> host sends RunRequest --> orchestrator validates typed request --> orchestrator obtains or builds committed run plan --> orchestrator requests node provisioning through ProvisionerActor --> provisioner reports nodes live/failure/logs --> worker node agents report runtime ready --> orchestrator waits for membership + route ownership --> orchestrator sends runtime-ready acknowledgements --> workers report runtime-ready acknowledgement --> orchestrator provisions stages --> workers report weights/stage ready --> orchestrator reports prompt-ready --> prompt messages are served through actors/token edges --> shutdown/fault/completion triggers actor teardown --> provisioner stops provider-owned worker leaves --> orchestrator reports torn down or faulted teardown result -``` - -The host may stop the engine only after the orchestrator actor/group has reached a terminal lifecycle state or after the host has declared the actor group failed. Engine shutdown itself is outside this specification. - -### 6.1 Request Validation - -The orchestrator must reject a typed run request before provisioning when required facts are missing or invalid. - -Examples: - -- missing run id; -- duplicate stage indexes; -- stage count of zero; -- stage count inconsistent with the committed plan; -- unknown node in a committed placement; -- missing model or tokenizer source required by stage provisioning; -- missing provider/provisioner actor address; -- missing orchestrator node id for token-edge endpoints; -- missing worker image/artifact reference required by the selected provider policy. - -Rejecting a run emits a typed lifecycle report and must not start workers. - -### 6.2 Planning - -Planning must complete before provisioning. - -If the orchestrator builds a run plan, the plan must be derived from trusted host-supplied model facts or locally inspectable model metadata supplied through a typed component. Workers do not negotiate placement after boot. - -A planning failure rejects the run before provisioning. - -### 6.3 Provisioning - -The orchestrator must request provisioning through actor-managed provider/provisioner leaves. It must not directly supervise worker processes as part of this contract. - -Provisioning request shape: - -```text -StartNodes { - nodes: Vec, - reply_to, -} -``` - -Each `NodeProvisionSpec` must include enough data for the provider leaf to start the worker runtime: - -```text -NodeProvisionSpec { - run_id, - node_id, - stage_index, - image_or_artifact_reference, - environment_or_runtime_facts, - mounts_or_resource_bindings, - coordinator_endpoint, - orchestrator_actor, -} -``` - -If provisioning one node fails after earlier nodes started, the actor group must attempt to stop already-started nodes before reporting startup failure. - -### 6.4 Runtime Readiness - -A runtime-ready actor report is necessary but not sufficient. - -A worker is runtime-ready only when all facts are true: - -```text -matching NodeRuntimeReady report -+ expected run_id / node_id / stage_index -+ SWIM member state is Alive for the worker endpoint node id -+ route owner for node_actor is the worker endpoint node id -+ no provider/node failure has been reported -= runtime readiness barrier passed for that worker -``` - -For planned pipeline execution, every expected stage worker must pass this barrier. For direct execution, the single expected worker must pass it. - -A TCP port, dashboard frame, provider log line, worker stdout line, or datastream frame must not satisfy runtime readiness. - -### 6.5 Runtime-Ready Acknowledgement - -After a worker passes the readiness barrier, the orchestrator sends: - -```text -NodeAgentMsg::RuntimeReadyAck { - run_id, - node_id, - stage_index, - readiness_id, -} -``` - -The orchestrator must keep actor/transport progress running while waiting for acknowledgement reports. - -If acknowledgement is not observed after the configured retry/timeout policy, startup fails. - -If the datastream publisher route is available, the orchestrator may subscribe to worker datastream output during this phase. Subscription success is observability setup, not readiness authority. - -### 6.6 Stage Provisioning - -Stage provisioning occurs after runtime-ready acknowledgement. - -Direct execution provisions one stage. Pipeline execution provisions stages from the committed run plan. - -Stage provision payloads must include: - -```text -run_id -orchestrator authority identity -node_id -stage_index -stage_count -layer range -inbound edge id and facts -outbound edge id and facts -model identity -GGUF/model source -tokenizer source -object specs -ring specs -consumer endpoint facts -``` - -A stage must reject unauthorized provisioning. `authorized_orchestrator` must be a stable orchestrator authority identity for the run, not a placeholder value. - -### 6.7 Weights and Stage Readiness - -A stage is ready only after the worker-side stage controller has observed all local readiness prerequisites: - -```text -valid provision accepted -+ worker runtime ready -+ weights ready -+ inbound edge ready -+ outbound edge ready -= StageReady -``` - -In planned pipeline execution, stages are weight-loaded/provisioned sequentially unless a later spec explicitly introduces parallel load behavior. The orchestrator must not advance to the next unloaded stage until the active stage reports weights ready or faults. - -### 6.8 Prompt Serving Readiness - -The orchestrator reports prompt-ready only after: - -```text -all expected workers provisioned -+ runtime readiness barriers passed -+ runtime-ready acknowledgements completed -+ stages provisioned -+ required weights/stages ready -+ token endpoints ready when pipeline mode uses them -``` - -Prompt-ready is an actor/datastream lifecycle state, not a TCP listener state. - ---- - -## 7. Prompt Behavior - -The orchestrator accepts at most one active prompt at a time. Additional prompt submissions remain pending in actor/mailbox order unless the actor group exposes a bounded queue and returns a typed `Fault` or rejection when full. - -Prompt events are matched by `request_id`. Events for a non-active request must not advance the active prompt. - -### 7.1 Direct Prompt Mode - -Direct mode is used when no pipeline token-edge runtime is active. - -Flow: - -```text -SubmitPrompt actor message --> active prompt state --> NodeAgentMsg::InferPrompt { request_id, prompt, max_tokens, reply_to } --> worker prompt engine --> PromptEvent actor messages to reply target --> terminal Done or Fault -``` - -The prompt reply target must be supplied to the worker node agent. A send failure to the node actor is a prompt-serving error for that request and may become a run fault if the worker path is no longer usable. - -### 7.2 Pipeline Prompt Mode - -Pipeline mode uses actor messages for tokenizer work and token-edge transport for generated token records. - -Flow: - -```text -SubmitPrompt actor message --> tokenizer encode actor --> TokenizerEvent::PromptEncoded --> token-in edge bytes --> pipeline stages --> token-out edge bytes --> tokenizer decode actor --> TokenizerEvent::TokensDecoded --> PromptEvent::TextDelta / Done / Fault -``` - -The first-stage node actor is the tokenizer encode actor unless the run request explicitly supplies a different tokenizer actor. The final-stage node actor is the tokenizer decode actor unless explicitly supplied otherwise. - -Token-edge bytes are data-plane traffic. They must not be carried in actor mailboxes. - -### 7.3 Token Sequence Rule - -Pipeline token output records must be consumed in strict sequence order. - -Expected rule: - -```text -first expected token-out sequence = 0 -received sequence must equal expected -on valid sequence: expected += 1 -on EOS or max_tokens: complete prompt -on mismatch: fault prompt or run according to phase policy -``` - -Sequence validation protects prompt output order and prevents feedback injection out of order. - -### 7.4 Prompt Terminal Rule - -A prompt ends with exactly one terminal event: - -```text -Done -Fault -``` - -Expected model/prompt failures should become prompt `Fault` events. Infrastructure failures that make the run unusable may also fault the run. - -After a terminal prompt event, the active prompt state is cleared and the next pending prompt may begin if the run is still prompt-ready. - ---- - -## 8. Shutdown and Teardown - -Shutdown begins from one of these actor-visible causes: - -- `RequestShutdown`; -- terminal prompt-serving policy for one-shot runs; -- stage fault; -- endpoint fault; -- membership loss; -- provider/node failure; -- host-declared fatal dependency failure. - -The orchestrator must emit or send teardown commands: - -```text -RunCommand::StopRun for each provisioned stage -RunCommand::TearDownTokenEndpoints when token endpoints exist -ProvisionerMsg::StopNodes for provider-owned workers -``` - -Worker stage teardown is complete only after every expected `StageStopped` report is observed. Token endpoint teardown is complete only after token endpoint stopped state is observed. Provider teardown is complete only after `ProvisionerReport::NodesStopped` or a typed provider stop failure is observed. - -`RunTornDown` is emitted once, after all required teardown facts are observed. - -Cleanup is best effort for external resources. A successful actor stop sequence proves only that the actor-managed stop calls completed. It does not prove that a cloud provider or OS removed every external resource. - ---- - -## 9. Error and Fault Model - -Errors are reported through typed lifecycle reports, prompt events, provisioner reports, and datastream records. - -### 9.1 Run Rejection - -Run rejection occurs before provisioning. Examples: - -- invalid typed run request; -- invalid committed plan; -- missing provisioner actor; -- missing required provider preparation result; -- missing model/tokenizer facts; -- unsupported provider policy; -- unsupported pipeline shape. - -A rejected run must not provision workers. - -### 9.2 Startup Fault - -Startup fault occurs after a run is accepted but before prompt-ready. Examples: - -- provisioning failure; -- worker exit before readiness; -- runtime-ready report mismatch for expected worker; -- membership never reaches alive state within policy; -- route owner never matches expected worker; -- runtime-ready acknowledgement timeout; -- stage provisioning send failure; -- stage fault while loading weights; -- provider failure before prompt-ready. - -Startup fault triggers teardown for any started workers. - -### 9.3 Prompt-Serving Fault - -Prompt-serving fault occurs after prompt-ready. Examples: - -- actor send failure to an active worker path; -- tokenizer actor send failure; -- tokenizer fault; -- token sequence violation; -- token record decode failure; -- worker exit during active serving; -- provider failure during active serving; -- membership loss for an active worker. - -A prompt-local fault may be returned as `PromptEvent::Fault` without faulting the entire run when the run remains usable. A worker/runtime fault must fault the run. - -### 9.4 Teardown Fault - -Teardown fault occurs when actor-managed stop/cleanup reports a failure. The orchestrator must continue attempting remaining stop actions and preserve the first stop failure for reporting. - -### 9.5 Secret Redaction - -Secret values must not appear in orchestrator-owned lifecycle, prompt, or datastream records. - -Secrets include: - -- provider API keys; -- Hugging Face tokens; -- SSH private-key material; -- provider credentials; -- raw bearer/session tokens. - -Secret presence may be reported as metadata. Secret values must not be copied. - ---- - -## 10. Actor Message Filtering - -Reports must match the active orchestration context before they can advance state. - -Filtering rules: - -- run-scoped reports must match `run_id`; -- worker reports must match an expected `node_id`; -- stage reports must match an expected `stage_index`; -- runtime-ready ack reports must match `readiness_id`; -- prompt events must match the active `request_id`; -- tokenizer events must match the active `request_id`; -- route ownership must match the worker endpoint node identity; -- membership facts must apply to the expected worker node identity. - -Mismatched reports are ignored, dropped, or reported as diagnostic observations according to phase policy. They must not make the active lifecycle progress. - ---- - -## 11. Datastream and Logs - -Datastream is the structured observation path. It is not the control path. - -The orchestrator actor/group may publish: - -- run accepted/rejected/faulted/completed/torn-down records; -- planning records; -- provisioning request/result records; -- runtime readiness barrier records; -- route and membership observations; -- prompt accepted/dispatched/delta/completed/faulted records; -- stage provision/ready/fault/stopped records; -- shutdown progress records; -- provider/provisioner log records received from actor-managed leaves; -- worker datastream frames collected through datastream subscriptions. - -Provider logs, worker stdout/stderr, managed-process lifecycle records, and dashboard frames are adapter-owned observations. They may be included in the orchestrator observation stream, but they do not become orchestrator actor inputs unless translated into typed actor reports by their owning actors. - -Frame archive output, when enabled by the host, is a datastream subscriber. It does not own lifecycle state and must not affect runtime behavior when absent. - ---- - -## 12. Verification Requirements - -Conforming systems must be verified by behavior, not by matching a preferred internal file layout. - -### 12.1 Boundary Checks - -Boundary compliance assertions: - -- the orchestrator actor does not define process exit status; -- prompt submission is actor message delivery, not TCP RPC; -- CLI/env/TOML parsing happens outside the actor contract; -- shutdown is not standard-input stop-word handling; -- the orchestrator actor does not own stdout/stderr; -- spawning the orchestrator actor does not execute a separate orchestrator binary; -- worker processes are supervised by host, provider, or process actors. - -### 12.2 Engine-Agnostic Spawn Check - -Start an engine with the reusable swactor + iroh-driver stack. Spawn the orchestrator actor/group into that engine, register its route, and drive only the generic pump: - -```text -tick_protocol_actors --> pump_inbound_to_actors --> runtime tick/run progress --> drain_outbox --> datastream adapter progress when enabled -``` - -Assert the orchestrator accepts a typed run request and emits actor reports without executing a separate orchestrator binary or binding prompt RPC. - -### 12.3 Actor Delivery Check - -Send orchestrator messages locally and, where applicable, through the iroh actor bridge. Assert reports arrive through actor reply targets/inboxes. Remote delivery must depend on codec registration and route ownership, not on hardcoded address construction. - -### 12.4 Run FSM Checks - -Keep or extend black-box FSM checks: - -- planning/provisioning starts only after pool/plan prerequisites; -- stage readiness and token endpoint readiness gate initial prompt injection; -- token feedback injects the next sequence only after consuming the previous sequence; -- EOS and max token limit stop generation; -- the first run fault is terminal and sticky; -- operator stop is terminal and distinct from fault; -- teardown emits stop commands and `RunTornDown` only after every stage and token endpoint stop is observed. - -### 12.5 Readiness Checks - -Verify prompt-ready is not emitted until: - -- expected runtime-ready actor reports arrive; -- SWIM membership is alive for each worker endpoint node id; -- route owner for each node actor matches that worker node id; -- runtime-ready acknowledgements are observed; -- stage provisioning is sent; -- weights/stage readiness is observed. - -Negative checks: - -- TCP port availability must not make readiness true; -- datastream frames must not make readiness true; -- provider log lines must not make readiness true; -- mismatched run/node/stage/readiness reports must not advance readiness. - -### 12.6 Provisioner Actor Checks - -With a stub provider/provisioner leaf: - -- `StartNodes` emits node start observations; -- node start failure reports `NodeFailed` and stops already-started nodes; -- plugin/adapter log observations emit `LogLine` and datastream log records; -- runtime-ready/bootstrap completion reports `NodeLive` only once; -- clean stop reports `NodesStopped`; -- stop failure preserves the first error while continuing stop attempts. - -### 12.7 Direct Prompt Checks - -Submit a direct prompt by actor message. Assert: - -- one active prompt; -- `InferPrompt` is sent to the expected node actor; -- prompt events are sent to the request reply target; -- mismatched request ids are ignored/dropped; -- terminal `Done` or `Fault` occurs exactly once; -- active prompt state clears after terminal event. - -### 12.8 Pipeline Prompt Checks - -Submit a pipeline prompt by actor message. Assert: - -- tokenizer encode request goes to the expected actor; -- encoded prompt enters token-in edge as sequence zero; -- generated token records from token-out are consumed in order; -- decode requests go to the expected actor; -- text deltas preserve request id; -- EOS and max token limit produce `Done`; -- tokenizer fault produces prompt `Fault`; -- token sequence violation faults prompt or run according to phase policy. - -### 12.9 Shutdown Checks - -Send actor shutdown. Assert: - -- no stdin, OS signal, or process-group control is required; -- stage stop commands are emitted for every provisioned stage; -- token endpoint teardown is emitted when endpoints exist; -- provider/provisioner stop is requested; -- all stop reports are required before `RunTornDown`; -- stop failures are reported while remaining stops continue. - -### 12.10 Datastream Non-Authority Checks - -Simulate logs and frames. Assert: - -- worker stdout/stderr/provider logs are recorded as observations; -- datastream frames can be archived or sent to dashboard sinks; -- logs/frames do not advance readiness, prompt completion, failure, or teardown; -- secret values are redacted from orchestrator-owned records. - -### 12.11 Wrapper/Host Boundary Checks - -For wrapper or host implementations that start an MVP run: - -- spawning the orchestrator actor does not resolve or execute a separate orchestrator binary; -- readiness is actor/datastream lifecycle readiness, not TCP connect success; -- prompt submission is actor message delivery; -- shutdown is actor/control shutdown; -- worker processes, if used, are managed leaves and not the orchestrator execution boundary. - ---- - -## 13. Out of Scope - -Out of scope for this orchestrator actor contract: - -- wrapper CLI UX; -- TOML/env parsing; -- Cargo artifact resolution; -- binary launch policy; -- process exit codes; -- prompt TCP compatibility adapters; -- OS signal handling; -- standard input command handling; -- stdout/stderr terminal behavior; -- actor scheduler internals; -- Tokio runtime lifecycle; -- iroh endpoint construction; -- QUIC/ALPN/stream internals; -- SWIM protocol internals beyond observed membership state; -- directory/registry internals beyond observed route ownership; -- datastream storage internals; -- dashboard rendering; -- provider marketplace semantics; -- Docker image construction; -- worker model execution internals; -- tokenizer correctness; -- model output quality; -- external resource cleanup guarantees after actor-managed stop requests complete; -- recovery after host/engine crash; -- multi-run orchestration in one actor group unless a later spec adds it. diff --git a/crates/mvp-system/src/actors/mod.rs b/crates/mvp-system/src/actors/mod.rs deleted file mode 100644 index 055ed51..0000000 --- a/crates/mvp-system/src/actors/mod.rs +++ /dev/null @@ -1,17 +0,0 @@ -//! swactor actor shells for the MVP system runtime. -//! -//! Each actor module owns its message type. Pure state machines remain in the -//! existing domain modules; actors translate mailbox messages into those cores and -//! report emitted commands/events back through actor messages. - -pub mod codec; -pub mod node_agent; -pub mod orchestrator; -pub mod stage_controller; - -pub fn register_mvp_actor_codecs(registry: &mut swactor_transport::CodecRegistry) { - node_agent::register_codecs(registry); - orchestrator::register_codecs(registry); - datastream::register_datastream_publisher_codec(registry); - crate::prompt_rpc::register_codecs(registry); -} diff --git a/crates/mvp-system/src/chat/config.rs b/crates/mvp-system/src/chat/config.rs new file mode 100644 index 0000000..ddbb8dc --- /dev/null +++ b/crates/mvp-system/src/chat/config.rs @@ -0,0 +1,7 @@ +pub const DEFAULT_CONFIG_PATH: &str = ".config/config.toml"; + +pub fn normalize_optional(value: Option) -> Option { + value + .map(|value| value.trim().to_owned()) + .filter(|value| !value.is_empty()) +} diff --git a/crates/mvp-system/src/chat.rs b/crates/mvp-system/src/chat/mod.rs similarity index 72% rename from crates/mvp-system/src/chat.rs rename to crates/mvp-system/src/chat/mod.rs index 63f3214..df869e4 100644 --- a/crates/mvp-system/src/chat.rs +++ b/crates/mvp-system/src/chat/mod.rs @@ -1,5 +1,7 @@ //! MVP operator chat wrapper public surface. +pub mod config; +pub mod node_image; mod runtime; pub use runtime::run_from_args; diff --git a/crates/mvp-system/src/node_image.rs b/crates/mvp-system/src/chat/node_image.rs similarity index 99% rename from crates/mvp-system/src/node_image.rs rename to crates/mvp-system/src/chat/node_image.rs index 7f646b3..fb8faa5 100644 --- a/crates/mvp-system/src/node_image.rs +++ b/crates/mvp-system/src/chat/node_image.rs @@ -319,6 +319,7 @@ fn prepare_node_image_inner( fn workspace_root() -> Result { let output = Command::new("git") .args(["rev-parse", "--show-toplevel"]) + .current_dir(env!("CARGO_MANIFEST_DIR")) .stdin(Stdio::null()) .output() .map_err(|e| format!("locate repository root with git: {e}"))?; diff --git a/crates/mvp-system/src/chat/runtime.rs b/crates/mvp-system/src/chat/runtime.rs index d7ee457..379e38c 100644 --- a/crates/mvp-system/src/chat/runtime.rs +++ b/crates/mvp-system/src/chat/runtime.rs @@ -22,15 +22,15 @@ use signal_hook::consts::signal::{SIGINT, SIGTERM}; #[cfg(target_os = "linux")] use signal_hook::iterator::Signals; -use crate::config as chat_config; -use crate::config::ResolvedVastAiConfig; -use crate::node_image::{ +use crate::chat::config as chat_config; +use crate::chat::node_image::{ NodeImageProgressEvent, NodeImageProgressEventKind, NodeImageProgressSink, NodeImageProvider, NodeImageRequest, PreparedNodeImage, prepare_node_image_with_progress, }; -use crate::observability::{benchmark_observability, frame_archive::FrameArchive}; +use crate::observability::{benchmark, frame_archive::FrameArchive}; use crate::orchestration::node_provisioning::{ProviderKind, provider_kind}; -use crate::prompt::prompt_rpc::{PromptEvent, SubmitPrompt, write_json_line}; +use crate::orchestration::provider_adapters::vastai::config::ResolvedVastAiConfig; +use crate::prompt::rpc::{PromptEvent, SubmitPrompt, write_json_line}; use crate::transport::endpoint_advertisement::EndpointAddrMask; const DEFAULT_RPC_ADDR: &str = "127.0.0.1:19777"; @@ -417,7 +417,7 @@ impl ChatDatastream { fn emit(&mut self, channel: &str, phase: &str, status: &str, detail: Value) { let id = self.channel_by_name(channel); - let benchmark = benchmark_observability::stamp("mvp-chat"); + let benchmark = benchmark::stamp("mvp-chat"); let payload = serde_json::to_vec(&json!({ "schema_version": benchmark["schema_version"].clone(), "type": "ChatProgress", @@ -445,7 +445,7 @@ impl ChatDatastream { fn emit_benchmark_envelope(&mut self, config: &Config) { let id = self.channel_by_name(CHAT_BENCHMARK_CHANNEL); - let benchmark = benchmark_observability::stamp("mvp-chat"); + let benchmark = benchmark::stamp("mvp-chat"); let payload = serde_json::to_vec(&json!({ "schema_version": benchmark["schema_version"].clone(), "type": "BenchmarkRunEnvelope", diff --git a/crates/mvp-system/src/lib.rs b/crates/mvp-system/src/lib.rs index 589de9b..dd7c7b1 100644 --- a/crates/mvp-system/src/lib.rs +++ b/crates/mvp-system/src/lib.rs @@ -3,32 +3,14 @@ #[cfg(test)] extern crate self as mvp_system; -mod actors; -mod arena_manager; -mod benchmark_observability; -mod bootstrap_datastream; pub mod chat; -mod config; -mod dashboard_view; -mod driver_pumps; -mod edge_establisher; -mod endpoint_advertisement; -mod engine_builder; -mod gpu_worker_ingress_parser; pub mod node; -mod node_boot_lifecycle; pub mod node_data; -mod node_image; pub mod observability; -mod observability_surface; pub mod orchestration; pub mod prompt; -mod prompt_rpc; -mod resource_inventory; pub mod staging; -mod telemetry; pub mod transport; -mod tx_rx_edge_actor; pub mod worker; #[cfg(test)] diff --git a/crates/mvp-system/src/node.rs b/crates/mvp-system/src/node.rs deleted file mode 100644 index a00279e..0000000 --- a/crates/mvp-system/src/node.rs +++ /dev/null @@ -1,22 +0,0 @@ -//! MVP node-local runtime public surface. -//! -//! Worker-node runtime behavior lives behind this module boundary; binaries -//! only wire entrypoints into it. - -pub mod worker_node_runtime; - -pub mod actor { - pub use crate::actors::node_agent::*; -} - -pub mod node_boot_lifecycle { - pub use crate::node_boot_lifecycle::*; -} - -pub mod node_image { - pub use crate::node_image::*; -} - -pub mod resource_inventory { - pub use crate::resource_inventory::*; -} diff --git a/crates/mvp-system/src/actors/node_agent.rs b/crates/mvp-system/src/node/actor.rs similarity index 99% rename from crates/mvp-system/src/actors/node_agent.rs rename to crates/mvp-system/src/node/actor.rs index d3733f6..d0a3863 100644 --- a/crates/mvp-system/src/actors/node_agent.rs +++ b/crates/mvp-system/src/node/actor.rs @@ -7,8 +7,8 @@ use swactor_transport::{CodecRegistry, NetworkMessage}; use crate::orchestration::run_plan; use crate::staging::{self as stage, gguf_shard::StageShardPlan}; -use super::codec::JsonCodec; -use super::orchestrator::OrchestratorMsg; +use crate::orchestration::actor::OrchestratorMsg; +use crate::transport::json_codec::JsonCodec; #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] pub enum StageEdgeKindWire { diff --git a/crates/mvp-system/src/node_boot_lifecycle.rs b/crates/mvp-system/src/node/boot_lifecycle.rs similarity index 100% rename from crates/mvp-system/src/node_boot_lifecycle.rs rename to crates/mvp-system/src/node/boot_lifecycle.rs diff --git a/crates/mvp-system/src/edge_establisher.rs b/crates/mvp-system/src/node/edge_lifecycle.rs similarity index 100% rename from crates/mvp-system/src/edge_establisher.rs rename to crates/mvp-system/src/node/edge_lifecycle.rs diff --git a/crates/mvp-system/src/node/mod.rs b/crates/mvp-system/src/node/mod.rs new file mode 100644 index 0000000..112c44c --- /dev/null +++ b/crates/mvp-system/src/node/mod.rs @@ -0,0 +1,9 @@ +//! MVP node-local runtime public surface. +//! +//! Worker-node runtime behavior lives behind this module boundary; binaries +//! only wire entrypoints into it. + +pub mod actor; +pub mod boot_lifecycle; +pub mod edge_lifecycle; +pub mod worker_node_runtime; diff --git a/crates/mvp-system/src/node/worker_node_runtime.rs b/crates/mvp-system/src/node/worker_node_runtime.rs index 5ff0beb..83b2e45 100644 --- a/crates/mvp-system/src/node/worker_node_runtime.rs +++ b/crates/mvp-system/src/node/worker_node_runtime.rs @@ -22,22 +22,22 @@ use datastream::{ Lifetime, NodeId, Record, StreamDescriptor, StreamId, StreamOrigin, }; -use crate::actors::register_mvp_actor_codecs; use crate::node::actor::{ NodeAgentActor, NodeAgentMsg, NodeAgentReport, StageCommandWire, StageInboundEdgeWire, StageObjectSpecWire, StageOutboundEdgeWire, StageRingSpecWire, }; +use crate::node::edge_lifecycle as edge; use crate::node_data::arena; use crate::node_data::ingress; -use crate::node_data::local_transport as driver_model; -use crate::observability::benchmark_observability; +use crate::observability::benchmark; use crate::orchestration::distribution_stack::DistributionRuntimeStack; use crate::orchestration::provider_adapters::relay::relay_runtime_config_from_env; use crate::orchestration::run_plan::{GgufSource, TokenizerSource}; -use crate::prompt::prompt_rpc::{PromptEvent, TokenizerEvent}; +use crate::prompt::rpc::{PromptEvent, TokenizerEvent}; use crate::staging::control as stage; use crate::staging::gguf_shard::{StageShardPlan, materialize_stage_shard_http}; -use crate::transport::edge_establisher as edge; +use crate::transport::codec_registry::register_mvp_actor_codecs; +use crate::transport::driver_pumps as driver_model; use crate::transport::endpoint_advertisement::{ EndpointAddrMask, MVP_IROH_ENDPOINT_ADDR_MASK_ENV, advertised_endpoint, }; @@ -75,7 +75,7 @@ const NODE_SAMPLER_CHANNEL: &str = "mvp.node.sampler"; const WORKER_COMMAND_WAIT_TELEMETRY_INTERVAL: Duration = Duration::from_secs(1); fn worker_benchmark_stamp(run_id: u64, node_id: u64) -> Value { - let mut benchmark = benchmark_observability::stamp("mvp-worker-node"); + let mut benchmark = benchmark::stamp("mvp-worker-node"); let pid = benchmark .get("producer_process_id") .and_then(Value::as_u64) diff --git a/crates/mvp-system/src/node_data.rs b/crates/mvp-system/src/node_data.rs deleted file mode 100644 index 8d9e9f6..0000000 --- a/crates/mvp-system/src/node_data.rs +++ /dev/null @@ -1,42 +0,0 @@ -//! MVP node-local data-plane adapter public surface. -//! -//! Reusable arena, ring, and object-record contracts live in `data-plane`. -//! This module is the MVP boundary that binds those contracts to worker ingress, -//! worker egress, edge actors, and local transport behavior. - -pub mod arena { - pub use crate::arena_manager::*; -} - -pub mod ring { - pub use data_plane::ring::*; -} - -pub mod object { - pub use crate::gpu_worker_ingress_parser::{ - FLAG_BEGIN_SEQUENCE, FLAG_END_OF_SEQUENCE, HEADER_LEN, KNOWN_FLAGS_MASK, OBJECT_MAGIC, - OBJECT_MAGIC_BYTES, OBJECT_VERSION, ObjectFailureReason, ObjectFlags, ObjectHeader, - ObjectId, ObjectLayout, ObjectRecord, ObjectRecordBuilder, ObjectRecordRead, ObjectSpec, - read_object_record, - }; -} - -pub mod ingress { - pub use crate::gpu_worker_ingress_parser::*; -} - -pub mod egress { - pub use crate::worker::egress::*; -} - -pub mod local_transport { - pub use crate::driver_pumps::*; -} - -pub mod edge_actor { - pub use crate::tx_rx_edge_actor::*; -} - -pub mod reusable { - pub use data_plane::{arena, object_record, ring}; -} diff --git a/crates/mvp-system/src/arena_manager.rs b/crates/mvp-system/src/node_data/arena.rs similarity index 100% rename from crates/mvp-system/src/arena_manager.rs rename to crates/mvp-system/src/node_data/arena.rs diff --git a/crates/mvp-system/src/tx_rx_edge_actor.rs b/crates/mvp-system/src/node_data/edge_actor.rs similarity index 100% rename from crates/mvp-system/src/tx_rx_edge_actor.rs rename to crates/mvp-system/src/node_data/edge_actor.rs diff --git a/crates/mvp-system/src/gpu_worker_ingress_parser.rs b/crates/mvp-system/src/node_data/ingress.rs similarity index 100% rename from crates/mvp-system/src/gpu_worker_ingress_parser.rs rename to crates/mvp-system/src/node_data/ingress.rs diff --git a/crates/mvp-system/src/node_data/mod.rs b/crates/mvp-system/src/node_data/mod.rs new file mode 100644 index 0000000..40b5c4c --- /dev/null +++ b/crates/mvp-system/src/node_data/mod.rs @@ -0,0 +1,25 @@ +//! MVP node-local data-plane adapter public surface. +//! +//! Reusable arena, ring, and object-record contracts live in `data-plane`. +//! This module binds those contracts to MVP worker ingress, worker egress, +//! and edge actor behavior. + +pub mod arena; +pub mod edge_actor; +pub mod ingress; + +pub mod ring { + pub use data_plane::ring::*; +} + +pub mod object { + pub use data_plane::object_record::*; +} + +pub mod egress { + pub use crate::worker::egress::*; +} + +pub mod reusable { + pub use data_plane::{arena, object_record, ring}; +} diff --git a/crates/mvp-system/src/observability.rs b/crates/mvp-system/src/observability.rs deleted file mode 100644 index 89b6509..0000000 --- a/crates/mvp-system/src/observability.rs +++ /dev/null @@ -1,19 +0,0 @@ -//! MVP observability public surface. - -pub mod frame_archive; - -pub mod benchmark_observability { - pub use crate::benchmark_observability::*; -} - -pub mod dashboard_view { - pub use crate::dashboard_view::*; -} - -pub mod observability_surface { - pub use crate::observability_surface::*; -} - -pub mod telemetry { - pub use crate::telemetry::*; -} diff --git a/crates/mvp-system/src/benchmark_observability.rs b/crates/mvp-system/src/observability/benchmark.rs similarity index 100% rename from crates/mvp-system/src/benchmark_observability.rs rename to crates/mvp-system/src/observability/benchmark.rs diff --git a/crates/mvp-system/src/dashboard_view.rs b/crates/mvp-system/src/observability/dashboard_view.rs similarity index 99% rename from crates/mvp-system/src/dashboard_view.rs rename to crates/mvp-system/src/observability/dashboard_view.rs index b31607f..2380868 100644 --- a/crates/mvp-system/src/dashboard_view.rs +++ b/crates/mvp-system/src/observability/dashboard_view.rs @@ -10,7 +10,7 @@ use serde::Serialize; use serde_json::{Value, json}; use std::sync::RwLock; -use crate::observability::observability_surface as obs; +use crate::observability::lifecycle as obs; use crate::observability::telemetry::{ MVP_LIFECYCLE, MVP_PROVISIONING_EVENTS, MVP_PROVISIONING_LOGS, MvpLifecycleRecord, MvpProvisionEventRecord, MvpProvisionLogRecord, diff --git a/crates/mvp-system/src/observability/frame_archive.rs b/crates/mvp-system/src/observability/frame_archive.rs index dfa743a..aee3a07 100644 --- a/crates/mvp-system/src/observability/frame_archive.rs +++ b/crates/mvp-system/src/observability/frame_archive.rs @@ -5,7 +5,7 @@ use std::path::{Path, PathBuf}; use datastream::{Frame, StreamId}; use serde_json::json; -use crate::observability::benchmark_observability; +use crate::observability::benchmark; /// JSONL archive for MVP-owned datastream frames. /// @@ -61,7 +61,7 @@ impl FrameArchive { }; let record = json!({ "arrival_seq": self.next_seq, - "arrival_unix_ms": benchmark_observability::unix_ms_now(), + "arrival_unix_ms": benchmark::unix_ms_now(), "source": source, "stream": stream.to_string(), "channel": channel, diff --git a/crates/mvp-system/src/observability_surface.rs b/crates/mvp-system/src/observability/lifecycle.rs similarity index 100% rename from crates/mvp-system/src/observability_surface.rs rename to crates/mvp-system/src/observability/lifecycle.rs diff --git a/crates/mvp-system/src/observability/mod.rs b/crates/mvp-system/src/observability/mod.rs new file mode 100644 index 0000000..be9e2bb --- /dev/null +++ b/crates/mvp-system/src/observability/mod.rs @@ -0,0 +1,8 @@ +//! MVP observability public surface. + +pub mod benchmark; +pub mod dashboard_view; +pub mod frame_archive; +pub mod lifecycle; +pub mod provisioning_logs; +pub mod telemetry; diff --git a/crates/mvp-system/src/bootstrap_datastream.rs b/crates/mvp-system/src/observability/provisioning_logs.rs similarity index 100% rename from crates/mvp-system/src/bootstrap_datastream.rs rename to crates/mvp-system/src/observability/provisioning_logs.rs diff --git a/crates/mvp-system/src/telemetry.rs b/crates/mvp-system/src/observability/telemetry.rs similarity index 96% rename from crates/mvp-system/src/telemetry.rs rename to crates/mvp-system/src/observability/telemetry.rs index 1c823f6..60aa214 100644 --- a/crates/mvp-system/src/telemetry.rs +++ b/crates/mvp-system/src/observability/telemetry.rs @@ -4,8 +4,8 @@ use datastream::hardware::net::HostNetSample; use datastream::{ChannelRegistry, Record}; use serde::{Deserialize, Serialize}; -use crate::arena_manager::ArenaSample; -use crate::observability::observability_surface as obs; +use crate::node_data::arena::ArenaSample; +use crate::observability::lifecycle as obs; use crate::orchestration::provisioning::{self, ProvisionLogStream}; /// Structured MVP lifecycle facts: run, node, stage, edge, ring, object, step, and worker events. diff --git a/crates/mvp-system/src/actors/orchestrator.rs b/crates/mvp-system/src/orchestration/actor.rs similarity index 99% rename from crates/mvp-system/src/actors/orchestrator.rs rename to crates/mvp-system/src/orchestration/actor.rs index 8de4a3d..c57002d 100644 --- a/crates/mvp-system/src/actors/orchestrator.rs +++ b/crates/mvp-system/src/orchestration/actor.rs @@ -6,7 +6,7 @@ use swactor_transport::{CodecRegistry, NetworkMessage}; use crate::orchestration::run_fsm as core; -use super::codec::JsonCodec; +use crate::transport::json_codec::JsonCodec; #[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct StageRefWire { diff --git a/crates/mvp-system/src/orchestration/app.rs b/crates/mvp-system/src/orchestration/app.rs index 8194eb3..89be1e5 100644 --- a/crates/mvp-system/src/orchestration/app.rs +++ b/crates/mvp-system/src/orchestration/app.rs @@ -10,16 +10,16 @@ use std::sync::{Arc, mpsc}; use std::thread; use std::time::{Duration, Instant}; -use crate::actors::node_agent::{ +use crate::node::actor::{ NodeAgentMsg, StageEdgeKindWire, StageInboundEdgeWire, StageObjectSpecWire, StageOutboundEdgeWire, StageProvisionWire, StageRingSpecWire, }; -use crate::actors::orchestrator::{OrchestratorActor, OrchestratorReport}; -use crate::actors::register_mvp_actor_codecs; -use crate::config::{DEFAULT_CONFIG_PATH, TomlConfigOverlay}; #[cfg(feature = "dashboard")] use crate::observability::dashboard_view::MvpClusterDashboardView; -use crate::observability::{benchmark_observability, frame_archive::FrameArchive}; +use crate::observability::{benchmark, frame_archive::FrameArchive}; +use crate::orchestration::actor::{OrchestratorActor, OrchestratorReport}; +use crate::orchestration::config::{DEFAULT_CONFIG_PATH, TomlConfigOverlay}; +use crate::transport::codec_registry::register_mvp_actor_codecs; const PROVIDER_START_MAX_ATTEMPTS: usize = 4; use crate::node_data::object as ingress; @@ -46,7 +46,7 @@ use crate::orchestration::provisioning::{ }; use crate::orchestration::run_fsm::{RunConfig, RunId}; use crate::orchestration::run_plan::{self, GgufSource, TokenizerSource}; -use crate::prompt::prompt_rpc::{ +use crate::prompt::rpc::{ PromptEvent, SubmitPrompt, TokenizerEvent, read_submit_prompt, write_json_line, }; use crate::staging::gguf_shard::{StageShardPlan, plan_stage_shard}; @@ -4049,7 +4049,7 @@ impl OrchDatastream { status: &str, detail: Value, ) { - let benchmark = benchmark_observability::stamp("mvp-orchestrator"); + let benchmark = benchmark::stamp("mvp-orchestrator"); let payload = serde_json::to_vec(&json!({ "schema_version": benchmark["schema_version"].clone(), "type":"OrchBootstrap", @@ -4085,7 +4085,7 @@ impl OrchDatastream { status: &str, detail: Value, ) { - let benchmark = benchmark_observability::stamp("mvp-orchestrator"); + let benchmark = benchmark::stamp("mvp-orchestrator"); let payload = serde_json::to_vec(&json!({ "schema_version": benchmark["schema_version"].clone(), "type":"OrchPromptEvent", @@ -9139,8 +9139,8 @@ bootstrap_command = "/run" #[test] fn enqueue_runtime_ready_ack_reports_to_node_agent() { - use crate::actors::node_agent::{NodeAgentActor, NodeAgentReport}; - use crate::actors::orchestrator::OrchestratorMsg; + use crate::node::actor::{NodeAgentActor, NodeAgentReport}; + use crate::orchestration::actor::OrchestratorMsg; use crate::staging as stage; let stack = diff --git a/crates/mvp-system/src/config.rs b/crates/mvp-system/src/orchestration/config.rs similarity index 81% rename from crates/mvp-system/src/config.rs rename to crates/mvp-system/src/orchestration/config.rs index a94656d..8d0d330 100644 --- a/crates/mvp-system/src/config.rs +++ b/crates/mvp-system/src/orchestration/config.rs @@ -3,6 +3,8 @@ use std::path::Path; use serde::Deserialize; +use crate::orchestration::provider_adapters::vastai::config::VastAiConfig; + pub const DEFAULT_CONFIG_PATH: &str = ".config/config.toml"; /// Shared overlay for legacy and multi-binary configuration parsing. This accepts @@ -91,48 +93,6 @@ pub struct ObservabilityConfigOverlay { pub datastream_frame_log: Option, } -#[derive(Clone, Debug, Default, Deserialize, PartialEq)] -#[serde(default)] -pub struct VastAiConfig { - pub api_key: Option, - pub image: Option, - pub relay_url: Option, - pub bootstrap_command: Option, - pub disk_gb: Option, - pub ssh_user: Option, - pub confirm_lease: Option, - pub gpu_name: Option, - pub min_gpu_ram_mb: Option, - pub min_down_mbps: Option, - pub min_up_mbps: Option, - pub max_dph_total: Option, - pub min_reliability: Option, - pub require_verified: Option, - pub blacklist_hosts: Vec, - pub poll_interval_secs: Option, - pub onstart: Option, - pub ssh_identity: Option, -} - -#[derive(Clone, Debug, PartialEq)] -pub struct ResolvedVastAiConfig { - pub api_key: String, - pub relay_url: String, - pub image: String, - pub bootstrap_command: String, - pub disk_gb: Option, - pub gpu_name: Option, - pub min_gpu_ram_mb: Option, - pub min_down_mbps: Option, - pub min_up_mbps: Option, - pub max_dph_total: Option, - pub min_reliability: Option, - pub require_verified: Option, - pub blacklist_hosts: Vec, - pub onstart: Option, - pub ssh_identity: Option, -} - impl TomlConfigOverlay { pub fn load_optional(path: &Path) -> Result, String> { if path.is_file() { @@ -153,55 +113,12 @@ impl TomlConfigOverlay { } } -impl ResolvedVastAiConfig { - pub fn validate(self) -> Result { - require_non_empty("VAST_API_KEY", &self.api_key)?; - require_non_empty("relay.url", &self.relay_url)?; - require_non_empty("vastai.image", &self.image)?; - require_non_empty("vastai.bootstrap_command", &self.bootstrap_command)?; - if let Some(identity) = &self.ssh_identity { - require_non_empty("vastai.ssh_identity", identity)?; - } - if !looks_remote_image(&self.image) { - return Err(format!( - "vastai.image {:?} must include a registry namespace", - self.image - )); - } - Ok(self) - } -} - -fn require_non_empty(label: &str, value: &str) -> Result<(), String> { - if value.trim().is_empty() { - Err(format!("missing required {label}")) - } else { - Ok(()) - } -} - -pub fn normalize_optional(value: Option) -> Option { - value - .map(|value| value.trim().to_owned()) - .filter(|value| !value.is_empty()) -} - -pub fn looks_remote_image(image: &str) -> bool { - let repository = image.split('@').next().unwrap_or(image); - let last_slash = repository.rfind('/'); - let tag_separator = repository - .rfind(':') - .filter(|separator| last_slash.is_some_and(|slash| *separator > slash)); - let repository = tag_separator.map_or(repository, |separator| &repository[..separator]); - let Some((host, _)) = repository.split_once('/') else { - return false; - }; - host == "localhost" || host.contains('.') || host.contains(':') -} - #[cfg(test)] mod tests { use super::*; + use crate::orchestration::provider_adapters::vastai::config::{ + ResolvedVastAiConfig, looks_remote_image, + }; use std::sync::atomic::{AtomicU64, Ordering}; static TEMP_COUNTER: AtomicU64 = AtomicU64::new(0); diff --git a/crates/mvp-system/src/engine_builder/engine.rs b/crates/mvp-system/src/orchestration/engine_builder/engine.rs similarity index 100% rename from crates/mvp-system/src/engine_builder/engine.rs rename to crates/mvp-system/src/orchestration/engine_builder/engine.rs diff --git a/crates/mvp-system/src/engine_builder/error.rs b/crates/mvp-system/src/orchestration/engine_builder/error.rs similarity index 100% rename from crates/mvp-system/src/engine_builder/error.rs rename to crates/mvp-system/src/orchestration/engine_builder/error.rs diff --git a/crates/mvp-system/src/engine_builder/events.rs b/crates/mvp-system/src/orchestration/engine_builder/events.rs similarity index 100% rename from crates/mvp-system/src/engine_builder/events.rs rename to crates/mvp-system/src/orchestration/engine_builder/events.rs diff --git a/crates/mvp-system/src/engine_builder/launcher.rs b/crates/mvp-system/src/orchestration/engine_builder/launcher.rs similarity index 100% rename from crates/mvp-system/src/engine_builder/launcher.rs rename to crates/mvp-system/src/orchestration/engine_builder/launcher.rs diff --git a/crates/mvp-system/src/engine_builder/mod.rs b/crates/mvp-system/src/orchestration/engine_builder/mod.rs similarity index 100% rename from crates/mvp-system/src/engine_builder/mod.rs rename to crates/mvp-system/src/orchestration/engine_builder/mod.rs diff --git a/crates/mvp-system/src/engine_builder/model.rs b/crates/mvp-system/src/orchestration/engine_builder/model.rs similarity index 100% rename from crates/mvp-system/src/engine_builder/model.rs rename to crates/mvp-system/src/orchestration/engine_builder/model.rs diff --git a/crates/mvp-system/src/engine_builder/node_image.rs b/crates/mvp-system/src/orchestration/engine_builder/node_image.rs similarity index 100% rename from crates/mvp-system/src/engine_builder/node_image.rs rename to crates/mvp-system/src/orchestration/engine_builder/node_image.rs diff --git a/crates/mvp-system/src/engine_builder/planner.rs b/crates/mvp-system/src/orchestration/engine_builder/planner.rs similarity index 100% rename from crates/mvp-system/src/engine_builder/planner.rs rename to crates/mvp-system/src/orchestration/engine_builder/planner.rs diff --git a/crates/mvp-system/src/engine_builder/pool.rs b/crates/mvp-system/src/orchestration/engine_builder/pool.rs similarity index 100% rename from crates/mvp-system/src/engine_builder/pool.rs rename to crates/mvp-system/src/orchestration/engine_builder/pool.rs diff --git a/crates/mvp-system/src/engine_builder/roles.rs b/crates/mvp-system/src/orchestration/engine_builder/roles.rs similarity index 100% rename from crates/mvp-system/src/engine_builder/roles.rs rename to crates/mvp-system/src/orchestration/engine_builder/roles.rs diff --git a/crates/mvp-system/src/engine_builder/runtime_stack.rs b/crates/mvp-system/src/orchestration/engine_builder/runtime_stack.rs similarity index 97% rename from crates/mvp-system/src/engine_builder/runtime_stack.rs rename to crates/mvp-system/src/orchestration/engine_builder/runtime_stack.rs index 5739d5c..d665ee0 100644 --- a/crates/mvp-system/src/engine_builder/runtime_stack.rs +++ b/crates/mvp-system/src/orchestration/engine_builder/runtime_stack.rs @@ -7,12 +7,12 @@ use iroh::EndpointAddr; use iroh_driver::{IrohDriver, IrohDriverConfig}; use swactor::actor::ActorAddress; -use crate::actors::node_agent::NodeAgentActor; -use crate::actors::orchestrator::OrchestratorActor; -use crate::actors::register_mvp_actor_codecs; +use crate::node::actor::NodeAgentActor; +use crate::orchestration::actor::OrchestratorActor; use crate::orchestration::distribution_stack::DistributionRuntimeStack; use crate::orchestration::run_fsm as orchestrator_core; use crate::staging as stage_core; +use crate::transport::codec_registry::register_mvp_actor_codecs; pub struct RuntimeNodeConfig { pub distributed: DistributedNodeConfig, diff --git a/crates/mvp-system/src/engine_builder/workload.rs b/crates/mvp-system/src/orchestration/engine_builder/workload.rs similarity index 100% rename from crates/mvp-system/src/engine_builder/workload.rs rename to crates/mvp-system/src/orchestration/engine_builder/workload.rs diff --git a/crates/mvp-system/src/orchestration.rs b/crates/mvp-system/src/orchestration/mod.rs similarity index 86% rename from crates/mvp-system/src/orchestration.rs rename to crates/mvp-system/src/orchestration/mod.rs index 61e1e23..9168e36 100644 --- a/crates/mvp-system/src/orchestration.rs +++ b/crates/mvp-system/src/orchestration/mod.rs @@ -5,21 +5,18 @@ //! adapters live in `provider_adapters` so provider-neutral orchestration logic //! stays separate from local/Docker/VastAI implementation details. -pub mod actor { - pub use crate::actors::orchestrator::*; -} - +pub mod actor; pub mod app; +pub mod config; pub mod distribution_stack; +pub mod engine_builder; pub mod membership_readiness; pub mod node_provisioning; pub mod provisioning; +pub mod resource_inventory; pub mod run_fsm; pub mod run_plan; pub mod token_endpoint; -pub mod engine_builder { - pub use crate::engine_builder::*; -} pub mod provider_adapters { pub mod docker_cluster; diff --git a/crates/mvp-system/src/orchestration/provider_adapters/vastai/config.rs b/crates/mvp-system/src/orchestration/provider_adapters/vastai/config.rs new file mode 100644 index 0000000..1880413 --- /dev/null +++ b/crates/mvp-system/src/orchestration/provider_adapters/vastai/config.rs @@ -0,0 +1,83 @@ +use serde::Deserialize; + +#[derive(Clone, Debug, Default, Deserialize, PartialEq)] +#[serde(default)] +pub struct VastAiConfig { + pub api_key: Option, + pub image: Option, + pub relay_url: Option, + pub bootstrap_command: Option, + pub disk_gb: Option, + pub ssh_user: Option, + pub confirm_lease: Option, + pub gpu_name: Option, + pub min_gpu_ram_mb: Option, + pub min_down_mbps: Option, + pub min_up_mbps: Option, + pub max_dph_total: Option, + pub min_reliability: Option, + pub require_verified: Option, + pub blacklist_hosts: Vec, + pub poll_interval_secs: Option, + pub onstart: Option, + pub ssh_identity: Option, +} + +#[derive(Clone, Debug, PartialEq)] +pub struct ResolvedVastAiConfig { + pub api_key: String, + pub relay_url: String, + pub image: String, + pub bootstrap_command: String, + pub disk_gb: Option, + pub gpu_name: Option, + pub min_gpu_ram_mb: Option, + pub min_down_mbps: Option, + pub min_up_mbps: Option, + pub max_dph_total: Option, + pub min_reliability: Option, + pub require_verified: Option, + pub blacklist_hosts: Vec, + pub onstart: Option, + pub ssh_identity: Option, +} + +impl ResolvedVastAiConfig { + pub fn validate(self) -> Result { + require_non_empty("VAST_API_KEY", &self.api_key)?; + require_non_empty("relay.url", &self.relay_url)?; + require_non_empty("vastai.image", &self.image)?; + require_non_empty("vastai.bootstrap_command", &self.bootstrap_command)?; + if let Some(identity) = &self.ssh_identity { + require_non_empty("vastai.ssh_identity", identity)?; + } + if !looks_remote_image(&self.image) { + return Err(format!( + "vastai.image {:?} must include a registry namespace", + self.image + )); + } + Ok(self) + } +} + +fn require_non_empty(label: &str, value: &str) -> Result<(), String> { + if value.trim().is_empty() { + Err(format!("missing required {label}")) + } else { + Ok(()) + } +} + +pub fn looks_remote_image(image: &str) -> bool { + let repository = image.split('@').next().unwrap_or(image); + let last_slash = repository.rfind('/'); + let tag_separator = repository + .rfind(':') + .filter(|separator| last_slash.is_some_and(|slash| *separator > slash)); + let repository = tag_separator.map_or(repository, |separator| &repository[..separator]); + let Some((host, _)) = repository.split_once('/') else { + return false; + }; + host == "localhost" || host.contains('.') || host.contains(':') +} diff --git a/crates/mvp-system/src/orchestration/provider_adapters/vastai.rs b/crates/mvp-system/src/orchestration/provider_adapters/vastai/mod.rs similarity index 99% rename from crates/mvp-system/src/orchestration/provider_adapters/vastai.rs rename to crates/mvp-system/src/orchestration/provider_adapters/vastai/mod.rs index 8d19837..2c5b2f0 100644 --- a/crates/mvp-system/src/orchestration/provider_adapters/vastai.rs +++ b/crates/mvp-system/src/orchestration/provider_adapters/vastai/mod.rs @@ -1,3 +1,5 @@ +pub mod config; + use parking_lot::Mutex; use std::collections::{BTreeMap, BTreeSet, HashSet}; use std::io::{BufRead, BufReader, Read}; @@ -20,6 +22,7 @@ use swactor_vastai::{ SelectionPolicy, classify_vastai_error, create_instance, }; +use crate::observability::provisioning_logs::{BootstrapDatastreamBridge, node_stream_id}; use crate::orchestration::node_provisioning::{ CreateLeaseRequest, CreateLeaseResult, DestroyHandle, LeaseFacts, LogicalNodeSpec, ProviderError, ProviderLeaseId, ProviderPlugin, SshEndpoint, provider_kind, @@ -27,7 +30,6 @@ use crate::orchestration::node_provisioning::{ use crate::orchestration::provisioning::{ NodeProvisionSpec, PluginNodeHandle, PluginObservation, PluginSink, ProvisionPlugin, }; -use crate::transport::bootstrap_datastream::{BootstrapDatastreamBridge, node_stream_id}; #[derive(Clone, Debug)] pub struct VastAiProvisioningConfig { diff --git a/crates/mvp-system/src/orchestration/provisioning.rs b/crates/mvp-system/src/orchestration/provisioning.rs index 00dd225..bace936 100644 --- a/crates/mvp-system/src/orchestration/provisioning.rs +++ b/crates/mvp-system/src/orchestration/provisioning.rs @@ -22,7 +22,7 @@ pub use ::provisioning::plugin::{ ProvisionPlugin, }; -use crate::transport::bootstrap_datastream::BootstrapDatastreamBridge; +use crate::observability::provisioning_logs::BootstrapDatastreamBridge; pub struct LocalDockerPlugin { container_name_prefix: String, diff --git a/crates/mvp-system/src/resource_inventory.rs b/crates/mvp-system/src/orchestration/resource_inventory.rs similarity index 100% rename from crates/mvp-system/src/resource_inventory.rs rename to crates/mvp-system/src/orchestration/resource_inventory.rs diff --git a/crates/mvp-system/src/prompt.rs b/crates/mvp-system/src/prompt/mod.rs similarity index 60% rename from crates/mvp-system/src/prompt.rs rename to crates/mvp-system/src/prompt/mod.rs index ca0eccc..68c1067 100644 --- a/crates/mvp-system/src/prompt.rs +++ b/crates/mvp-system/src/prompt/mod.rs @@ -1,7 +1,5 @@ //! MVP prompt protocol public surface. -pub use crate::orchestration::token_endpoint; +pub mod rpc; -pub mod prompt_rpc { - pub use crate::prompt_rpc::*; -} +pub use crate::orchestration::token_endpoint; diff --git a/crates/mvp-system/src/prompt_rpc.rs b/crates/mvp-system/src/prompt/rpc.rs similarity index 98% rename from crates/mvp-system/src/prompt_rpc.rs rename to crates/mvp-system/src/prompt/rpc.rs index 8d66132..6cfeba6 100644 --- a/crates/mvp-system/src/prompt_rpc.rs +++ b/crates/mvp-system/src/prompt/rpc.rs @@ -3,7 +3,7 @@ use std::io::{BufRead, Write}; use serde::{Deserialize, Serialize}; use swactor_transport::{CodecRegistry, NetworkMessage}; -use crate::actors::codec::JsonCodec; +use crate::transport::json_codec::JsonCodec; #[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)] pub struct SubmitPrompt { diff --git a/crates/mvp-system/src/actors/stage_controller.rs b/crates/mvp-system/src/staging/actor.rs similarity index 100% rename from crates/mvp-system/src/actors/stage_controller.rs rename to crates/mvp-system/src/staging/actor.rs diff --git a/crates/mvp-system/src/staging.rs b/crates/mvp-system/src/staging/mod.rs similarity index 79% rename from crates/mvp-system/src/staging.rs rename to crates/mvp-system/src/staging/mod.rs index ba23f8d..549f42c 100644 --- a/crates/mvp-system/src/staging.rs +++ b/crates/mvp-system/src/staging/mod.rs @@ -1,5 +1,6 @@ //! MVP stage control, shard planning, and weight lifecycle public surface. +pub mod actor; pub mod control; pub mod gguf_metadata; pub mod gguf_shard; @@ -8,7 +9,4 @@ pub mod shard_weight_lifecycle; pub mod weight_lifecycle; pub mod weight_shards; -pub mod actor { - pub use crate::actors::stage_controller::*; -} pub use control::*; diff --git a/crates/mvp-system/src/tests/bootstrap_datastream_guarantees.rs b/crates/mvp-system/src/tests/bootstrap_datastream_guarantees.rs index 7ad319e..58140e0 100644 --- a/crates/mvp-system/src/tests/bootstrap_datastream_guarantees.rs +++ b/crates/mvp-system/src/tests/bootstrap_datastream_guarantees.rs @@ -2,11 +2,11 @@ use std::sync::Arc; use datastream::{DatastreamEndpoint, Record}; use iroh::{EndpointAddr, SecretKey}; +use mvp_system::observability::provisioning_logs::{BootstrapDatastreamBridge, node_stream_id}; use mvp_system::observability::telemetry::MvpProvisionLogRecord; use mvp_system::orchestration::provisioning::{ NodeProvisionSpec, PluginObservation, PluginObservationSink, PluginSink, ProvisionLogStream, }; -use mvp_system::transport::bootstrap_datastream::{BootstrapDatastreamBridge, node_stream_id}; use parking_lot::Mutex; use serde_json::json; diff --git a/crates/mvp-system/src/tests/driver_pumps_guarantees.rs b/crates/mvp-system/src/tests/driver_pumps_guarantees.rs index 1085c8f..1f16f50 100644 --- a/crates/mvp-system/src/tests/driver_pumps_guarantees.rs +++ b/crates/mvp-system/src/tests/driver_pumps_guarantees.rs @@ -8,7 +8,7 @@ //! They assert the guarantees in //! `specs/mvp_system/driver_pumps_contract.md`. -use mvp_system::node_data::local_transport as driver; +use mvp_system::transport::driver_pumps as driver; // A driver config names one endpoint and one ALPN. Tests do not expose tokio // tasks, connection internals, or stream futures to actors. diff --git a/crates/mvp-system/src/tests/edge_establisher_guarantees.rs b/crates/mvp-system/src/tests/edge_establisher_guarantees.rs index 5178929..1f0e029 100644 --- a/crates/mvp-system/src/tests/edge_establisher_guarantees.rs +++ b/crates/mvp-system/src/tests/edge_establisher_guarantees.rs @@ -9,7 +9,7 @@ //! They assert the guarantees in //! `specs/mvp_system/edge_establisher_contract.md`. -use mvp_system::transport::edge_establisher as edge; +use mvp_system::node::edge_lifecycle as edge; // A send provision carries the consumer node id because the driver must know // where to send. It deliberately carries no remote actor address. diff --git a/crates/mvp-system/src/tests/local_mock/assertions.rs b/crates/mvp-system/src/tests/local_mock/assertions.rs index 0047172..f9981b2 100644 --- a/crates/mvp-system/src/tests/local_mock/assertions.rs +++ b/crates/mvp-system/src/tests/local_mock/assertions.rs @@ -1,4 +1,4 @@ -use mvp_system::observability::observability_surface as obs; +use mvp_system::observability::lifecycle as obs; use super::mock_transport::MockObjectKind; diff --git a/crates/mvp-system/src/tests/local_mock/environment.rs b/crates/mvp-system/src/tests/local_mock/environment.rs index fd660ee..add6759 100644 --- a/crates/mvp-system/src/tests/local_mock/environment.rs +++ b/crates/mvp-system/src/tests/local_mock/environment.rs @@ -1,7 +1,7 @@ use std::collections::{BTreeMap, BTreeSet}; use mvp_system::node_data::edge_actor; -use mvp_system::observability::observability_surface as obs; +use mvp_system::observability::lifecycle as obs; use mvp_system::orchestration::engine_builder as engine; use mvp_system::orchestration::run_fsm as fsm; use mvp_system::orchestration::run_plan as plan; diff --git a/crates/mvp-system/src/tests/local_mock_pipeline_integration.rs b/crates/mvp-system/src/tests/local_mock_pipeline_integration.rs index dbd4e97..f5d8fce 100644 --- a/crates/mvp-system/src/tests/local_mock_pipeline_integration.rs +++ b/crates/mvp-system/src/tests/local_mock_pipeline_integration.rs @@ -5,7 +5,7 @@ //! and CUDA while still driving the run through planning, provisioning, //! readiness, prompt injection, stage execution, completion, and teardown. -use mvp_system::observability::observability_surface as obs; +use mvp_system::observability::lifecycle as obs; use mvp_system::orchestration::engine_builder as engine; use super::local_mock::{ diff --git a/crates/mvp-system/src/tests/module_surface_guarantees.rs b/crates/mvp-system/src/tests/module_surface_guarantees.rs index cbd97e8..4bee400 100644 --- a/crates/mvp-system/src/tests/module_surface_guarantees.rs +++ b/crates/mvp-system/src/tests/module_surface_guarantees.rs @@ -12,8 +12,8 @@ fn target_modules_offer_new_paths_to_existing_public_contracts() { let run_id: orchestration::run_plan::RunId = orchestration::run_plan::RunId::from(7); assert_eq!(run_id, orchestration::run_plan::RunId(7)); - let boot_node_id: node::node_boot_lifecycle::NodeId = node::node_boot_lifecycle::NodeId(11); - assert_eq!(boot_node_id, node::node_boot_lifecycle::NodeId(11)); + let boot_node_id: node::boot_lifecycle::NodeId = node::boot_lifecycle::NodeId(11); + assert_eq!(boot_node_id, node::boot_lifecycle::NodeId(11)); let arena_ring_id: node_data::arena::RingId = node_data::arena::RingId(3); assert_eq!(arena_ring_id, node_data::arena::RingId(3)); @@ -21,26 +21,21 @@ fn target_modules_offer_new_paths_to_existing_public_contracts() { let stage_edge_id: staging::EdgeId = staging::EdgeId(7001); assert_eq!(stage_edge_id, staging::EdgeId(7001)); - let transport_edge_id: transport::edge_establisher::EdgeId = - transport::edge_establisher::EdgeId(7002); - assert_eq!(transport_edge_id, transport::edge_establisher::EdgeId(7002)); + let transport_edge_id: node::edge_lifecycle::EdgeId = node::edge_lifecycle::EdgeId(7002); + assert_eq!(transport_edge_id, node::edge_lifecycle::EdgeId(7002)); let worker_generation: worker::WorkerGeneration = worker::WorkerGeneration(2); assert_eq!(worker_generation, worker::WorkerGeneration(2)); - let prompt_request: prompt::prompt_rpc::SubmitPrompt = prompt::prompt_rpc::SubmitPrompt { + let prompt_request: prompt::rpc::SubmitPrompt = prompt::rpc::SubmitPrompt { request_id: 42, prompt_text: "hello".into(), max_tokens: 8, }; assert_eq!(prompt_request.request_id, 42); - let observed_run_id: observability::observability_surface::RunId = - observability::observability_surface::RunId(7); - assert_eq!( - observed_run_id, - observability::observability_surface::RunId(7) - ); + let observed_run_id: observability::lifecycle::RunId = observability::lifecycle::RunId(7); + assert_eq!(observed_run_id, observability::lifecycle::RunId(7)); let _chat_entrypoint = chat::run_from_args::>; } diff --git a/crates/mvp-system/src/tests/node_boot_lifecycle_guarantees.rs b/crates/mvp-system/src/tests/node_boot_lifecycle_guarantees.rs index 48a0b86..decd15e 100644 --- a/crates/mvp-system/src/tests/node_boot_lifecycle_guarantees.rs +++ b/crates/mvp-system/src/tests/node_boot_lifecycle_guarantees.rs @@ -9,7 +9,7 @@ //! They assert the guarantees in //! `specs/mvp_system/node_boot_lifecycle_contract.md`. -use mvp_system::node::node_boot_lifecycle as boot; +use mvp_system::node::boot_lifecycle as boot; // A complete boot config lets the tests focus on ordering and failure behavior. // The concrete process, transport, arena, worker, and SWIM implementations are diff --git a/crates/mvp-system/src/tests/observability_surface_guarantees.rs b/crates/mvp-system/src/tests/observability_surface_guarantees.rs index e3ac460..d86527e 100644 --- a/crates/mvp-system/src/tests/observability_surface_guarantees.rs +++ b/crates/mvp-system/src/tests/observability_surface_guarantees.rs @@ -8,7 +8,7 @@ //! They assert the guarantees in //! `specs/mvp_system/observability_surface_contract.md`. -use mvp_system::observability::observability_surface as obs; +use mvp_system::observability::lifecycle as obs; // The trace fixture contains one successful run from boot through teardown. // Tests use structured events only; logs, transport, storage, and batching stay diff --git a/crates/mvp-system/src/tests/resource_inventory_guarantees.rs b/crates/mvp-system/src/tests/resource_inventory_guarantees.rs index 99c2d87..88558c0 100644 --- a/crates/mvp-system/src/tests/resource_inventory_guarantees.rs +++ b/crates/mvp-system/src/tests/resource_inventory_guarantees.rs @@ -10,7 +10,7 @@ //! They assert the guarantees in //! `specs/mvp_system/resource_inventory_contract.md`. -use mvp_system::node::resource_inventory as inventory; +use mvp_system::orchestration::resource_inventory as inventory; // The inventory fixture has more nodes than the placement needs. That proves // the planner may choose among known inventory entries but may not invent hidden diff --git a/crates/mvp-system/src/tests/telemetry_guarantees.rs b/crates/mvp-system/src/tests/telemetry_guarantees.rs index 4e233cf..48686f0 100644 --- a/crates/mvp-system/src/tests/telemetry_guarantees.rs +++ b/crates/mvp-system/src/tests/telemetry_guarantees.rs @@ -1,6 +1,6 @@ use datastream::{ChannelId, ChannelKind, Frame, Lifetime, Record, StreamId}; use mvp_system::observability::frame_archive::FrameArchive; -use mvp_system::observability::observability_surface as obs; +use mvp_system::observability::lifecycle as obs; use mvp_system::observability::telemetry::{ self, MvpLifecycleRecord, MvpProvisionEventRecord, MvpProvisionLogRecord, }; diff --git a/crates/mvp-system/src/transport.rs b/crates/mvp-system/src/transport.rs deleted file mode 100644 index e931118..0000000 --- a/crates/mvp-system/src/transport.rs +++ /dev/null @@ -1,13 +0,0 @@ -//! MVP edge transport public surface. - -pub mod bootstrap_datastream { - pub use crate::bootstrap_datastream::*; -} - -pub mod edge_establisher { - pub use crate::edge_establisher::*; -} - -pub mod endpoint_advertisement { - pub use crate::endpoint_advertisement::*; -} diff --git a/crates/mvp-system/src/transport/codec_registry.rs b/crates/mvp-system/src/transport/codec_registry.rs new file mode 100644 index 0000000..a85f21d --- /dev/null +++ b/crates/mvp-system/src/transport/codec_registry.rs @@ -0,0 +1,11 @@ +//! MVP runtime codec registration. +//! +//! Actor behavior lives in the owning domain modules. This module wires their +//! message codecs into the transport registry used by distributed runtimes. + +pub fn register_mvp_actor_codecs(registry: &mut swactor_transport::CodecRegistry) { + crate::node::actor::register_codecs(registry); + crate::orchestration::actor::register_codecs(registry); + datastream::register_datastream_publisher_codec(registry); + crate::prompt::rpc::register_codecs(registry); +} diff --git a/crates/mvp-system/src/driver_pumps.rs b/crates/mvp-system/src/transport/driver_pumps.rs similarity index 100% rename from crates/mvp-system/src/driver_pumps.rs rename to crates/mvp-system/src/transport/driver_pumps.rs diff --git a/crates/mvp-system/src/endpoint_advertisement.rs b/crates/mvp-system/src/transport/endpoint_advertisement.rs similarity index 100% rename from crates/mvp-system/src/endpoint_advertisement.rs rename to crates/mvp-system/src/transport/endpoint_advertisement.rs diff --git a/crates/mvp-system/src/actors/codec.rs b/crates/mvp-system/src/transport/json_codec.rs similarity index 100% rename from crates/mvp-system/src/actors/codec.rs rename to crates/mvp-system/src/transport/json_codec.rs diff --git a/crates/mvp-system/src/transport/mod.rs b/crates/mvp-system/src/transport/mod.rs new file mode 100644 index 0000000..0619ab9 --- /dev/null +++ b/crates/mvp-system/src/transport/mod.rs @@ -0,0 +1,6 @@ +//! MVP edge transport public surface. + +pub mod codec_registry; +pub mod driver_pumps; +pub mod endpoint_advertisement; +pub mod json_codec; diff --git a/crates/mvp-system/src/worker.rs b/crates/mvp-system/src/worker/mod.rs similarity index 100% rename from crates/mvp-system/src/worker.rs rename to crates/mvp-system/src/worker/mod.rs