#pragma once // The seam between the Scheduler and CORE's engine. Everything the Scheduler drives on the // engine goes through this interface, so the Scheduler is unit-testable without a live // engine and the daemon is not bound to the concrete `vdm::Engine`. The real // implementation (engine_port_core, added when velox::core's stage-8 bodies reach main) // wraps `vdm::Engine` + `vdm::segment::SegmentBudget`; FakeEnginePort records calls. // // Task ids here are `vdm::TaskId` โ€” the engine assigns one from start() and the Scheduler // keeps the wire-UUID <-> TaskId map (ADR 0013). Admission is the Scheduler's: for a task // the governor admits, it probes first (persisting sizeBytes/resumable/validator before a // single byte moves), then calls start() with that ProbeResult as probe_hint. The min-1 // fairness rule in SegmentBudget then guarantees each started task a slot; set_task_order // pushes the priority. #include #include #include #include #include #include "vdm/ids.hpp" #include "vdm/net/probe.hpp" #include "vdm/task/download.hpp" #include "vdm/util/result.hpp" namespace velox::daemon::sched { class EnginePort { public: virtual ~EnginePort() = default; // Runs on the probe pool, outside the segment budget (ADR 0011 ยง5); `done` arrives on // an engine thread, exactly once. The Scheduler probes before every start() so it // always has a real ProbeResult (size, resumable, validator) to persist and to pass // back as DownloadSpec.probe_hint โ€” one code path instead of "sometimes has one". virtual void probe(const vdm::net::ProbeRequest& req, std::function)> done) = 0; virtual vdm::TaskId start(const vdm::task::DownloadSpec& spec, vdm::task::DownloadCallbacks callbacks) = 0; virtual void pause(vdm::TaskId) = 0; virtual void resume(vdm::TaskId) = 0; virtual void cancel(vdm::TaskId, bool discard_partial) = 0; virtual void provide_auth(vdm::TaskId, const std::string& username, const std::string& password, bool remember) = 0; virtual void decide(vdm::TaskId, vdm::task::Decision) = 0; virtual void refresh_url(vdm::TaskId, const std::string& url, const std::vector& headers = {}) = 0; // The daemon is done with this task (it went terminal). Drop the handle. Idempotent. virtual void release(vdm::TaskId) = 0; // A synchronous, lock-guarded snapshot (DownloadHandle::progress()). nullopt if the // id is unknown (already released, or never started). virtual std::optional progress(vdm::TaskId) const = 0; // ADR 0011 admission surface. Values are DAEMON's; enforcement is the engine's. virtual void set_task_order(const std::vector& order) = 0; virtual void set_max_active_segments(std::uint32_t n) = 0; virtual void set_host_segment_cap(const std::string& host, std::uint32_t cap) = 0; // limiter.set: 0 means unlimited (vdm::rate::TokenBucket's own convention), applied // across every active transfer immediately โ€” there is no "next task only" variant for // a single shared global bucket, so `applyToRunning` on the wire has nothing to select // between; it is accepted for schema compliance and always behaves as if true. virtual void set_global_speed_limit(std::uint64_t bps) = 0; }; } // namespace velox::daemon::sched