Expand description
GET /v1/tasks/:id/runs/:run/steps* (the metadata + content debug
plane over a Run’s step OUTPUT — McpQueryAdapter, a server-side
mlua_swarm::core::projection::ProjectionAdapter impl reading through
the Data-plane OutputStore with a persisted RunRecord.result_ref
fallback). See the module doc for how this relates to
operator_ws::session’s in-flight FileProjectionAdapter hook and
worker’s Worker-axis context.steps pointer assembly.
McpQueryAdapter — server-side [ProjectionAdapter], and the REST
hierarchy that serves a Run’s step OUTPUT as metadata + content
(projection-adapter ST5’s HTTP debug plane — replaces the ST2/ST4
GET /v1/tasks/:id/ctx single-value endpoint / ProjectionResponse).
§Two consumers, two roles (ST5)
- Worker axis (
crates/mlua-swarm-server/src/worker.rs’sGET /v1/worker/prompthandler) — the primary supply path. A worker’s fetch payload carriescontext.steps: Vec<StepPointer>, aContextPolicy.steps-filtered pointer list assembled automatically at fetch time; no separate tool call needed. - HTTP debug plane (this module’s
GET /v1/tasks/:id/runs/:run/steps*routes) — the content the above pointers’content_urladdresses, plus an unfiltered metadata/content view for operators / humans debugging a run.
§GH #23 subtask-3: table-driven addressing (replaces the runtime union
rule)
Both consumers share McpQueryAdapter::list_steps’s enumeration.
Previously every distinct step_ref name in RunRecord.step_entries
was resolved through the Data-plane OutputStore, unioned with
RunRecord.result_ref’s top-level object keys (the finalized-Run
fallback), Data-plane winning a name collision — the pre-GH-#23 runtime
union rule. That rule is now statically replaced: every real
Compiler::compile output carries a
mlua_swarm::core::step_naming::StepNaming table (built once, at
compile time — see that module’s doc), and this module’s enumeration /
single-key resolution ([Self::enumerate_steps] /
[Self::resolve_async]) look the table up via
Engine::step_naming_for and report every step under its ONE canonical
name, addressable by that name OR any alias (Step.ref / the out
ctx-path’s top-level segment). The runtime union / collision-priority
logic itself no longer runs per-request; it is baked into the table
once, at register time (StepNaming::from_blueprint).
[Self::enumerate_steps_legacy_union] keeps the OLD runtime-union body
verbatim as a defensive-only fallback for the rare case no table
resolves (a spawn stack the dispatcher never wired
EngineDispatcher::with_step_naming for — certain test harnesses that
seed OutputStore/RunStore fixtures directly without driving a real
dispatch); it is not a “declared Blueprints get the new path, undeclared
ones keep the old one” branch — undeclared Blueprints get the SAME
table-driven path (their canonical name is simply their own Step.ref,
byte-identical to the pre-GH-#23 name).
§Architecture (subtask-4 rework, carried into ST5, table-driven since
subtask-3)
McpQueryAdapter reads through two backings, tried in order, for
every step’s OWN dispatch (RunRecord.step_entries row → its own
StepId):
- Data-plane, in-flight-safe AND Run-scoped (subtask-4’s original
reason for being; Run-scoped since subtask-3 — see the former KNOWN
LIMITATION below):
McpQueryAdapter::resolve_async/ [Self::enumerate_steps_via_table] look upOutputStore::get_latest_by_name_in_run(step_entry.step_id, 1, canonical_name)— the same storeEngine::submit_output’s submit-time projection sink dual-writes into (seemlua_swarm::core::engine::Engine::submit_output’s doc), keyed Run-scoped by construction (aStepIdis globally unique per dispatch, so two concurrent Runs sharing a producer name never cross-resolve — no narrowing-by-guard needed any more). A hit here can be a not-yet-finalized Run’s already-submitted step — the in-flight case subtask-4 exists for. - Persisted
RunRecord.result_reffallback (unchanged in kind, now tried under the canonical name AND every alias): used whenever (1) comes back empty (no Data-plane record for that step’s own dispatch yet — e.g. a Run that predates the engine having anOutputStorewired).
Unlike crate::operator_ws::session‘s spawn-time
mlua_swarm::core::projection::FileProjectionAdapter hook (which
materializes the spawning agent’s own AgentContextView), this
adapter’s Data-plane path serves prior steps’ submitted OUTPUT —
the pull-supply counterpart to Engine’s submit-time file sink.
§Former KNOWN LIMITATION (closed by GH #23 subtask-3)
OutputStore::get_latest_by_name is producer-name-scoped, not
Run-scoped (see mlua_swarm::store::output’s module doc) — it returns
the single newest Final submitted anywhere under that producer name,
across every Run / Task, so two concurrent Runs whose flow.ir happens
to dispatch an agent of the identical name could race each other. This
module no longer calls that method: every lookup here goes through
OutputStore::get_latest_by_name_in_run, scoped to the dispatching
step’s own (globally unique) StepId, closing the race by
construction, independent of whether the Blueprint declared a
projection_name (see
mlua_swarm::store::output::OutputStore::get_latest_by_name_in_run’s
doc). get_latest_by_name itself is untouched (still used by
Engine::submit_output’s own fail-open cross-Run compatibility path)
— only this module’s consumption of it changed.
[ProjectionAdapter::fetch] is a synchronous trait method, but this
adapter’s backing stores are async. McpQueryAdapter::resolve_async
is the real, native-async implementation; [step_content] (the
content-plane HTTP handler) calls McpQueryAdapter::list_steps
directly. [ProjectionAdapter::fetch] instead bridges to
McpQueryAdapter::resolve_async via tokio::task::block_in_place +
Handle::block_on purely for trait conformance (dependency inversion —
this adapter implements the same core::projection::ProjectionAdapter
trait mlua_swarm::core::projection::FileProjectionAdapter does, so a
caller holding a dyn ProjectionAdapter can use either
polymorphically); the hot HTTP path never takes that bridge.
Structs§
- McpQuery
Adapter - Server-side
ProjectionAdapterbacked by anOutputStore(in-flight-safe, subtask-4, Run-scoped since GH #23 subtask-3) with aRunStore-backedresult_reffallback (see the module doc for the full narrative). Holds anEnginehandle (GH #23 subtask-3) soSelf::step_naming_for_runcan pull the Blueprint-wideStepNamingtableEngine::step_naming_forsnapshotted at dispatch time. - Step
List - Response body for
GET /v1/tasks/:id/runs/:run/steps. - Step
Path Query - Query params shared by the metadata and content routes: narrows a
single step’s value via
$.a.bdot-path form (the leading$.is optional) — same syntaxmlua_swarm::core::projection::ProjectionKeyalready establishes. - Step
Summary - One step’s metadata (operator / debug plane) —
GET /v1/tasks/:id/runs/:run/steps/:step, and each entry ofStepList::steps.
Enums§
- Projection
Source - Which backing produced a
StepSummary/ a Worker-axisStepPointer— Data-plane wins a name collision (module doc’s “Architecture” section).
Functions§
- step_
content GET /v1/tasks/:id/runs/:run/steps/:step/content?path=$.a.b— the raw body: full bytes, no envelope, no Range support.Content-TypeandETagfollowStepSummary::content_type/StepSummary::sha256’s same rules (module doc). GH #23 subtask-3::stepis canonicalized the same waystep_getdoes.- step_
get GET /v1/tasks/:id/runs/:run/steps/:step?path=$.a.b— one step’s metadata, optionally narrowed. GH #23 subtask-3::stepis canonicalized (adapter.resolve_step_name) before the lookup, so either the canonical name or any alias 200s — the reportedStepSummary::nameis always the canonical form.- steps_
list GET /v1/tasks/:id/runs/:run/steps— every step visible for the addressed Run, unfiltered (see the module doc’s role split).