Skip to main content

Module projection

Module projection 

Source
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/prompt fetch payload carries crate::core::agent_context::AgentContextView::steps: Vec<crate::core::agent_context::StepPointer>, a ContextPolicy.steps-filtered pointer list assembled automatically at fetch time by crates/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-declared mlua_swarm_schema::ContextPolicy::steps / steps_exclude.
  • HTTP debug plane (metadata + content)crates/mlua-swarm-server/src/projection.rs‘s GET /v1/tasks/:id/runs/:run/steps* REST hierarchy is the content the pointers’ content_url addresses, plus an unfiltered metadata/content view for operators / humans debugging a run. The old mse_ctx_get MCP tool (a manual pull wrapper over the ST2/ST4 GET /v1/tasks/:id/ctx single-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§

FileProjectionAdapter
File-backed ProjectionAdapter: materializes the projected value under root, at the target ProjectionPlacement::target_path resolves for key, and reads it back on Self::fetch. Self::new resolves through ProjectionPlacement::default (the pre-GH-#27 hardcoded <root>/workspace/tasks/<task_id>/ctx/<step-or-_ctx>.md layout, unchanged); Self::with_placement resolves through a caller-supplied resolver instead — see crate::core::projection_placement’s module doc for the “3 path” convergence this collapses.
ProjectionKey
Addressing for one projection target. ID axis (task_id / run_id) + Name axis (step) + Path axis (path) — the one addressing type both FileProjectionAdapter and the future McpQueryAdapter (ST2) accept.

Enums§

ProjectionError
All ways a ProjectionAdapter operation can fail.
ProjectionRef
ProjectionAdapter::project’s return value — the locator a worker uses to pull the projected value.

Traits§

ProjectionAdapter
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).