Expand description
ProjectionAdapter — pull-based supply of step OUTPUT data to
downstream Agent steps, materialized from run Ctx
state.
§Architecture (ST5: role separation, MCP tool retired)
Worker MCP dispatch stores each step’s OUTPUT in the run ctx
(Ctx.data while a run is live, RunRecord.result_ref once a Run has
finalized on the server). projection-adapter ST5 settles this
module’s role split around two axes, replacing the ST2-ST4 single
mse_ctx_get MCP tool:
- Worker axis (primary supply) — a worker’s
GET /v1/worker/promptfetch payload carriescrate::core::agent_context::AgentContextView::steps:Vec<crate::core::agent_context::StepPointer>, aContextPolicy.steps-filtered pointer list assembled automatically at fetch time bycrates/mlua-swarm-server/src/worker.rs. No separate MCP tool call needed — a fetch already has every visible prior step’s pointer, pre-filtered by the Blueprint-declaredmlua_swarm_schema::ContextPolicy::steps/steps_exclude. - HTTP debug plane (metadata + content) —
crates/mlua-swarm-server/src/projection.rs‘sGET /v1/tasks/:id/runs/:run/steps*REST hierarchy is the content the pointers’content_urladdresses, plus an unfiltered metadata/content view for operators / humans debugging a run. The oldmse_ctx_getMCP tool (a manual pull wrapper over the ST2/ST4GET /v1/tasks/:id/ctxsingle-value endpoint) is retired: its entire reason for being — a way to pull a prior step’s OUTPUT on demand — is now automatic on the Worker axis.
ProjectionAdapter::project turns a ProjectionKey (identifying a
slice of run ctx by task / run / step / field path) plus the
policy-filtered ctx data into a ProjectionRef locator; a caller
later calls ProjectionAdapter::fetch to retrieve the actual value.
The directive header only ever carries
ProjectionAdapter::pointer_line’s 1-3 line pointer — never the
projected value itself (no inline full-embed) — the SAME pointer-only
discipline AgentContextView.steps follows on the Worker axis.
The run ctx / RunRecord.result_ref stays the single source of truth;
an adapter only decides how the pull is served — as a materialized
file (FileProjectionAdapter, this module, still used for the
spawning agent’s own AgentContextView — see
crates/mlua-swarm-server/src/operator_ws/session.rs’s
append_projection_pointer) or as an MCP query endpoint
(McpQueryAdapter, server-side, see ProjectionRef::Query). Both
adapters share the one ProjectionKey addressing type so callers
never need to branch on which adapter backs a given pointer.
§Submit-path projection (subtask-4 / ST2 rework, carried into ST5)
The subtask-4 rework adds a third, submit-triggered supply path,
sitting beside the two above rather than replacing either: the moment a
worker’s Final output lands in crate::core::engine::Engine::submit_output
/ submit_worker_result_trusted (the canonical worker-submit path, POST /v1/worker/submit and /v1/worker/result), the engine materializes that
step’s OUTPUT to the crate::core::projection_placement::ProjectionPlacement
resolver’s target (<root>/<dir_template>/<canonical_agent>.md; the
byte-compat default layout is workspace/tasks/<task_id>/ctx/) via
FileProjectionAdapter::materialize_submission — this is the file a
Worker-axis StepPointer.file_path (when Some) and the HTTP debug
plane’s StepSummary.file_path both address, so a later Agent step
(or an operator, over HTTP) can read a prior step’s OUTPUT while the
overall run is still in flight, without waiting for
RunRecord.result_ref to be set at finalization. <root> is resolved
from the crate::core::agent_context::AgentContextView
AgentContextMiddleware snapshotted at spawn time, via
ProjectionPlacement::resolve_root (work_dir falling back to
project_root for the byte-compat default preference); this sink is
best-effort (fail-open — see the Invariants on Engine::submit_output’s
doc): an unresolved root, or a Final from a spawn that never ran
through that middleware, is a silent no-op, never a submit failure.
<canonical_agent> (GH #23) is producer_agent (TaskState.spec.agent)
resolved through Engine::step_naming_for(task_id)’s
crate::core::step_naming::StepNaming::canonical_of_producer when a
table was snapshotted for this task’s dispatch — producer_agent
unchanged (byte-identical file stem to pre-GH-#23 behavior) for any
step whose AgentMeta.projection_name is undeclared, or when no table
exists for this task_id at all.
§Design: addressing
ProjectionKey combines three independent axes — ID (task_id /
run_id), Name (step), and Path (path, a $.a.b-style dot path
into the step’s OUTPUT value) — so a caller can address anything from
“the whole run ctx” down to “one field of one step’s OUTPUT” with the
same type. ProjectionKey::resolve is the pure, adapter-independent
implementation of that narrowing, shared by every adapter.
Structs§
- File
Projection Adapter - File-backed
ProjectionAdapter: materializes the projected value underroot, at the targetProjectionPlacement::target_pathresolves forkey, and reads it back onSelf::fetch.Self::newresolves throughProjectionPlacement::default(the pre-GH-#27 hardcoded<root>/workspace/tasks/<task_id>/ctx/<step-or-_ctx>.mdlayout, unchanged);Self::with_placementresolves through a caller-supplied resolver instead — seecrate::core::projection_placement’s module doc for the “3 path” convergence this collapses. - Projection
Key - Addressing for one projection target. ID axis (
task_id/run_id) + Name axis (step) + Path axis (path) — the one addressing type bothFileProjectionAdapterand the futureMcpQueryAdapter(ST2) accept.
Enums§
- Projection
Error - All ways a
ProjectionAdapteroperation can fail. - Projection
Ref ProjectionAdapter::project’s return value — the locator a worker uses to pull the projected value.
Traits§
- Projection
Adapter - Context projection supply abstraction. The single source of truth is
the run ctx passed into
Self::project; an adapter only decides how the pull is served (module doc has the full narrative).