# `wf.source` Resource Ref Helpers Implementation Plan > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [x]`) syntax for tracking. **Goal:** Add explicit source-aware helper capabilities under `wf.source` for pass-by-value resource refs using `logical_source`, starting with bounded `read_resource`. **Architecture:** Resource refs are inert JSON data moved by normal workflow input/state/output bindings. Core bindings do not inspect or rewrite refs. Only explicit `wf.source` helper nodes dereference refs by resolving `logical_source` through runtime/platform context, then reading the concrete source via the existing source content access path. This plan depends on the platform source policy plan so `wf.source` does not require self-binding. **Tech Stack:** Python 3.14, Pydantic models, `wf_authoring.node`, `RuntimeContext`, `WorkflowOperationContext`, `SourceCatalogService`/content access, pytest. --- ## Preconditions - Complete `docs/historical/superpowers/plans/2026-06-13-platform-source-policy.md` first. - `CapabilitySource(policy=SourcePolicy(platform=True, binding_required=False))` must exist. - `wf.std` deployments should validate/run without `wf.std=wf.std` self-bindings. --- ## File Structure - Modify `src/wf_core/run_state.py`: add optional platform runtime context to `RuntimeContext`. - Create `src/wf_api/platform_context.py`: small protocol/model for resolving logical sources and reading resources. - Modify `src/wf_core/runtime/engine.py`, `src/wf_core/runtime/step.py`, and `src/wf_core/runtime/ops/nodes.py`: thread the opaque platform object from workflow execution entrypoints into every `RuntimeContext`, including concurrent foreach item execution. - Modify `src/wf_api/runtime_dependencies.py` and server runtime runners: include source binding map/platform context in runtime dependencies. - Create `src/wf_api/source_refs.py`: Pydantic `SourceResourceRef` and bounded output model. - Create `src/wf_api/source_helpers.py`: `wf.source.read_resource` NodeSpec factory. - Modify `src/wf_api/local_sources.py` and MCP server composition to register `wf.source`. - Test: - `tests/wf_api/test_source_refs.py` - `tests/wf_api/test_source_helpers.py` - `tests/wf_server/test_local_static_server.py` - `tests/wf_transport_rpc_http/test_mcp_backed_server_rpc.py` --- ### Task 1: Add Source Ref Models **Files:** - Create: `src/wf_api/source_refs.py` - Modify: `src/wf_api/__init__.py` - Test: `tests/wf_api/test_source_refs.py` - [x] **Step 1: Write model tests** Create `tests/wf_api/test_source_refs.py`: ```python from __future__ import annotations import pytest from wf_api.source_refs import SourceResourceRef def test_source_resource_ref_requires_logical_source_and_uri() -> None: ref = SourceResourceRef( logical_source="drive", uri="gdrive://file/abc", mime_type="application/pdf", name="Report.pdf", ) assert ref.kind == "source_resource_ref" assert ref.logical_source == "drive" assert ref.uri == "gdrive://file/abc" assert ref.model_dump(mode="json")["name"] == "Report.pdf" def test_source_resource_ref_rejects_empty_logical_source() -> None: with pytest.raises(ValueError): SourceResourceRef(logical_source="", uri="gdrive://file/abc") ``` - [x] **Step 2: Run tests and confirm failure** Run: ```bash uv run pytest tests/wf_api/test_source_refs.py -q ``` Expected: fails because `wf_api.source_refs` does not exist. - [x] **Step 3: Implement models** Create `src/wf_api/source_refs.py`: ```python from __future__ import annotations from typing import Literal from pydantic import BaseModel, Field class SourceResourceRef(BaseModel): """Workflow-safe resource handle. The ref is inert pass-by-value data. Only explicit source-aware helper nodes dereference it through deployment source bindings and platform context. """ kind: Literal["source_resource_ref"] = "source_resource_ref" logical_source: str = Field(min_length=1) uri: str = Field(min_length=1) mime_type: str | None = None name: str | None = None ``` In `src/wf_api/__init__.py`, export `SourceResourceRef`. - [x] **Step 4: Run tests and commit** Run: ```bash uv run pytest tests/wf_api/test_source_refs.py -q uv run basedpyright --level error src/wf_api/source_refs.py tests/wf_api/test_source_refs.py ``` Expected: tests pass and typecheck has 0 errors. Commit: ```bash git add src/wf_api/source_refs.py src/wf_api/__init__.py tests/wf_api/test_source_refs.py git commit -m "feat: add source resource ref model" ``` --- ### Task 2: Add Platform Context To RuntimeContext **Files:** - Modify: `src/wf_core/run_state.py` - Create: `src/wf_api/platform_context.py` - Modify: `src/wf_core/runtime/engine.py` - Modify: `src/wf_core/runtime/step.py` - Modify: `src/wf_core/runtime/ops/nodes.py` - Test: `tests/core/test_runtime_context.py` or existing nearest runtime context test - [x] **Step 1: Add context tests** Create `tests/core/test_runtime_context.py` if no equivalent exists: ```python from __future__ import annotations from wf_core import RuntimeContext class _Platform: pass def test_runtime_context_can_carry_platform_context() -> None: platform = _Platform() ctx = RuntimeContext(current_node_id="node", platform=platform) assert ctx.current_node_id == "node" assert ctx.platform is platform ``` - [x] **Step 2: Run test and confirm failure** Run: ```bash uv run pytest tests/core/test_runtime_context.py -q ``` Expected: fails because `RuntimeContext` has no `platform` field. - [x] **Step 3: Add platform field** In `src/wf_core/run_state.py`, update `RuntimeContext`: ```python @dataclass(slots=True) class RuntimeContext: current_node_id: str platform: object | None = None ``` If it is not a dataclass, preserve the current structure and add `platform: object | None = None`. Create `src/wf_api/platform_context.py`: ```python from __future__ import annotations from typing import Any, Protocol class WorkflowPlatformContext(Protocol): """Runtime platform services available only to explicit platform helper nodes.""" def resolve_source(self, logical_source: str) -> str: ... async def read_resource( self, *, source_id: str, uri: str, max_chars: int, ) -> dict[str, Any]: ... ``` - [x] **Step 4: Pass platform context through async node execution** Thread an opaque `platform: object | None = None` through the async runtime entrypoints: - `src/wf_core/runtime/engine.py` - `execute_workflow_async(...)` - `execute_workflow_result_async(...)` - `resume_workflow_async(...)` - `resume_workflow_result_async(...)` - `src/wf_core/runtime/step.py` - `step_workflow_async(...)` - `_step_async_foreach_item_batch(...)` or the nearest helper that calls `invoke_node_use_async_for_frame(...)` - `src/wf_core/runtime/ops/nodes.py` - `_resolve_node_execution(...)` - `execute_node_use_async(...)` - `invoke_node_use_async_for_frame(...)` At the final context construction seam, pass: ```python RuntimeContext( current_node_id=node.id, ..., platform=platform, ) ``` Keep synchronous runtime support unchanged unless tests prove it also needs the field. The first `wf.source` helper is async and should be exercised through the async workflow API. Do not add source-specific code to `wf_core`. The field is an opaque object. Add one focused async-runtime test that calls `execute_workflow_result_async(..., platform=sentinel)` with a node handler that asserts `ctx.platform is sentinel`. - [x] **Step 5: Run tests and commit** Run: ```bash uv run pytest tests/core/test_runtime_context.py tests/authoring/test_nodes.py tests/authoring/test_async_runtime.py -q uv run basedpyright --level error src/wf_core/run_state.py src/wf_core/runtime/engine.py src/wf_core/runtime/step.py src/wf_core/runtime/ops/nodes.py src/wf_api/platform_context.py tests/core/test_runtime_context.py ``` Expected: tests pass and typecheck has 0 errors. Commit: ```bash git add src/wf_core/run_state.py src/wf_core/runtime/engine.py src/wf_core/runtime/step.py src/wf_core/runtime/ops/nodes.py src/wf_api/platform_context.py tests/core/test_runtime_context.py git commit -m "feat: carry platform context through runtime context" ``` --- ### Task 3: Build Source Platform Context In API Runtime **Files:** - Modify: `src/wf_api/runtime_dependencies.py` - Modify: `src/wf_server/context.py` - Modify: `src/wf_mcp/broker/service/workflow_runtime.py` - Test: `tests/wf_api/test_runtime_dependencies.py` or nearest existing file - [x] **Step 1: Add tests for source resolution** Create `tests/wf_api/test_platform_context.py`: ```python from __future__ import annotations import pytest from wf_api.platform_context import SourceBindingPlatformContext def test_platform_context_resolves_logical_source() -> None: context = SourceBindingPlatformContext( source_bindings={"drive": "drive.personal"}, read_resource_handler=None, ) assert context.resolve_source("drive") == "drive.personal" def test_platform_context_uses_identity_for_platform_sources() -> None: context = SourceBindingPlatformContext( source_bindings={}, platform_sources={"wf.source"}, read_resource_handler=None, ) assert context.resolve_source("wf.source") == "wf.source" def test_platform_context_rejects_unbound_source() -> None: context = SourceBindingPlatformContext(source_bindings={}, read_resource_handler=None) with pytest.raises(KeyError, match="unbound logical source"): context.resolve_source("drive") ``` - [x] **Step 2: Run tests and confirm failure** Run: ```bash uv run pytest tests/wf_api/test_platform_context.py -q ``` Expected: fails because `SourceBindingPlatformContext` does not exist. - [x] **Step 3: Implement platform context** In `src/wf_api/platform_context.py`, add: ```python from collections.abc import Awaitable, Callable, Mapping from dataclasses import dataclass, field ReadResourceHandler = Callable[[str, str, int], Awaitable[dict[str, Any]]] @dataclass(frozen=True, slots=True) class SourceBindingPlatformContext: """Resolve logical source refs for explicit source-aware helper nodes.""" source_bindings: Mapping[str, str] read_resource_handler: ReadResourceHandler | None platform_sources: set[str] = field(default_factory=set) def resolve_source(self, logical_source: str) -> str: if logical_source in self.platform_sources: return logical_source try: return self.source_bindings[logical_source] except KeyError as exc: raise KeyError(f"unbound logical source {logical_source!r}") from exc async def read_resource( self, *, source_id: str, uri: str, max_chars: int, ) -> dict[str, Any]: if self.read_resource_handler is None: raise RuntimeError("source resource reads are not configured") return await self.read_resource_handler(source_id, uri, max_chars) ``` - [x] **Step 4: Thread platform context through workflow runners** Update `LocalWorkflowRuntimeRunner.prepare_workflow_runtime()` in `src/wf_server/context.py` to also create a platform context from deployment bindings: ```python platform_context = SourceBindingPlatformContext( source_bindings={} if deployment is None else deployment.binding_map(), platform_sources={ source_id for source_id, source in self.specs.capability_sources.items() if source.policy.platform }, read_resource_handler=None, ) ``` Return it from `prepare_workflow_runtime()` by extending the tuple: ```python return ( workflow, dependencies.node_registry, dependencies.reducers, prepared_subgraphs, platform_context, ) ``` Then pass `platform=platform_context` into `execute_workflow_result_async()` and `resume_workflow_result_async()` if those functions accept it after Task 2. If Task 2 used a narrower internal function instead, adapt at that seam. Do the equivalent in the MCP-backed runtime runner in `src/wf_mcp/broker/service/workflow_runtime.py`. `WorkflowRuntimeService` currently owns source/catalog runtime state but not `ContentAccessService`, so add a small optional constructor field: ```python read_resource_handler: ReadResourceHandler | None = None ``` When building the platform context, pass that handler through: ```python async def _read_resource(source_id: str, uri: str, max_chars: int) -> dict[str, Any]: if self.read_resource_handler is None: raise RuntimeError("source resource reads are not configured") return await self.read_resource_handler(source_id, uri, max_chars) ``` Wire that field from `WfMcpService`/server construction after Task 4 adds the content-access helper. If `read_resource_by_source_uri` does not exist yet, commit the neutral/local platform context in this task and finish MCP wiring in Task 4. - [x] **Step 5: Run tests and commit** Run: ```bash uv run pytest tests/wf_api/test_platform_context.py tests/wf_server/test_local_static_server.py -q uv run basedpyright --level error src/wf_api/platform_context.py src/wf_server/context.py src/wf_mcp/broker/service/workflow_runtime.py tests/wf_api/test_platform_context.py ``` Expected: tests pass and typecheck has 0 errors. If MCP runner wiring needs Task 4 helper first, commit only the neutral/local platform context in this task and document the MCP wiring in the next task. Commit: ```bash git add src/wf_api/platform_context.py src/wf_server/context.py src/wf_mcp/broker/service/workflow_runtime.py tests/wf_api/test_platform_context.py git commit -m "feat: build source binding platform context" ``` --- ### Task 4: Add Bounded Source Resource Read Helper **Files:** - Create: `src/wf_api/source_helpers.py` - Modify: `src/wf_mcp/broker/service/content_access.py` - Test: `tests/wf_api/test_source_helpers.py` - Test: `tests/wf_mcp/service/test_content_access.py` - [x] **Step 1: Add helper tests** Create `tests/wf_api/test_source_helpers.py`: ```python from __future__ import annotations import pytest from wf_api.platform_context import SourceBindingPlatformContext from wf_api.source_helpers import read_resource from wf_api.source_refs import SourceResourceRef from wf_core import RuntimeContext async def test_read_resource_resolves_logical_source_and_bounds_text() -> None: calls: list[tuple[str, str, int]] = [] async def handler(source_id: str, uri: str, max_chars: int): calls.append((source_id, uri, max_chars)) return { "contents": [ { "type": "text", "text": "abcdefghijklmnopqrstuvwxyz", "mimeType": "text/plain", } ] } platform = SourceBindingPlatformContext( source_bindings={"drive": "drive.personal"}, read_resource_handler=handler, ) result = await read_resource( SourceResourceRef(logical_source="drive", uri="gdrive://file/abc"), RuntimeContext(current_node_id="read", platform=platform), max_chars=5, ) assert calls == [("drive.personal", "gdrive://file/abc", 5)] assert result.truncated is True assert result.text == "abcde" async def test_read_resource_requires_platform_context() -> None: with pytest.raises(RuntimeError, match="platform context"): await read_resource( SourceResourceRef(logical_source="drive", uri="gdrive://file/abc"), RuntimeContext(current_node_id="read"), ) ``` - [x] **Step 2: Run tests and confirm failure** Run: ```bash uv run pytest tests/wf_api/test_source_helpers.py -q ``` Expected: fails because `source_helpers.py` does not exist. - [x] **Step 3: Implement bounded helper** Create `src/wf_api/source_helpers.py`: ```python from __future__ import annotations from typing import cast from pydantic import BaseModel, Field from wf_core import RuntimeContext from .platform_context import WorkflowPlatformContext from .source_refs import SourceResourceRef class ReadResourceOutput(BaseModel): """Bounded resource read result suitable for workflow state/output.""" source_id: str uri: str mime_type: str | None = None text: str | None = None content_count: int truncated: bool = False async def read_resource( ref: SourceResourceRef, ctx: RuntimeContext, *, max_chars: int = 4000, ) -> ReadResourceOutput: """Explicitly dereference one source resource ref through platform context.""" platform = ctx.platform if platform is None: raise RuntimeError("wf.source.read_resource requires platform context") typed_platform = cast(WorkflowPlatformContext, platform) source_id = typed_platform.resolve_source(ref.logical_source) payload = await typed_platform.read_resource( source_id=source_id, uri=ref.uri, max_chars=max_chars, ) contents = payload.get("contents", []) first = contents[0] if isinstance(contents, list) and contents else {} text = first.get("text") if isinstance(first, dict) else None if isinstance(text, str) and len(text) > max_chars: text = text[:max_chars] truncated = True else: truncated = False mime_type = ( first.get("mimeType") if isinstance(first, dict) and isinstance(first.get("mimeType"), str) else ref.mime_type ) return ReadResourceOutput( source_id=source_id, uri=ref.uri, mime_type=mime_type, text=text if isinstance(text, str) else None, content_count=len(contents) if isinstance(contents, list) else 0, truncated=truncated, ) ``` If basedpyright complains that `ctx.platform` is `object`, use a runtime `Protocol` check or a local `cast(WorkflowPlatformContext, platform)` with a comment: ```python from typing import cast typed_platform = cast(WorkflowPlatformContext, platform) ``` - [x] **Step 4: Add source-uri content access helper** In `src/wf_mcp/broker/service/content_access.py`, add: ```python async def read_resource_by_source_uri( self, *, source_id: str, uri: str, max_chars: int, ) -> dict[str, Any]: """Read one provider URI from a concrete source for wf.source helpers.""" resource = next( ( entry for entry in self.source_catalog.list_resources(connection_id=source_id) if entry.uri == uri ), None, ) if resource is None: raise KeyError(f"unknown resource {uri!r} for source {source_id!r}") return await self.upstream.read_resource( self.source_catalog.connection_lookup(source_id), resource.qualified_name, resource.uri, ) ``` Preserve the `max_chars` parameter in the signature even if truncation is performed by `wf_api.source_helpers`; this keeps the platform seam explicit. - [x] **Step 5: Run tests and commit** Run: ```bash uv run pytest tests/wf_api/test_source_helpers.py tests/wf_mcp/service/test_content_access.py -q uv run ruff check src/wf_api/source_helpers.py src/wf_mcp/broker/service/content_access.py tests/wf_api/test_source_helpers.py tests/wf_mcp/service/test_content_access.py uv run basedpyright --level error src/wf_api/source_helpers.py src/wf_mcp/broker/service/content_access.py tests/wf_api/test_source_helpers.py tests/wf_mcp/service/test_content_access.py ``` Expected: tests pass, ruff clean, typecheck 0 errors. Commit: ```bash git add src/wf_api/source_helpers.py src/wf_mcp/broker/service/content_access.py tests/wf_api/test_source_helpers.py tests/wf_mcp/service/test_content_access.py git commit -m "feat: add bounded source resource reader" ``` --- ### Task 5: Register `wf.source` Platform Source **Files:** - Modify: `src/wf_api/local_sources.py` - Modify: `src/wf_mcp/broker/service/core.py` or source registration seam - Test: `tests/wf_server/test_local_static_server.py` - Test: `tests/wf_transport_rpc_http/test_mcp_backed_server_rpc.py` - [x] **Step 1: Add source inventory test** In `tests/wf_server/test_local_static_server.py`, add: ```python def test_local_static_server_exposes_wf_source_platform_source(tmp_path) -> None: server = build_local_static_workflow_server(tmp_path) source = server.context.specs.capability_sources["wf.source"] assert source.policy.platform is True assert source.policy.binding_required is False assert "wf.source.read_resource" in source.capabilities.node_specs ``` - [x] **Step 2: Run test and confirm failure** Run: ```bash uv run pytest tests/wf_server/test_local_static_server.py::test_local_static_server_exposes_wf_source_platform_source -q ``` Expected: fails because `wf.source` is not registered. - [x] **Step 3: Register source** In `src/wf_api/local_sources.py`, import: ```python from wf_authoring import node from wf_api.source_helpers import ReadResourceOutput, read_resource from wf_api.source_refs import SourceResourceRef ``` Add a node wrapper that exposes `max_chars` as input: ```python class ReadResourceInput(BaseModel): ref: SourceResourceRef max_chars: int = Field(default=4000, ge=1, le=20000) @node(name="read_resource") async def read_resource_node( payload: ReadResourceInput, ctx: RuntimeContext, ) -> ReadResourceOutput: return await read_resource(payload.ref, ctx, max_chars=payload.max_chars) ``` Register a `CapabilitySource`: ```python CapabilitySource( id="wf.source", kind="system", capabilities=CapabilityBuckets( node_specs={"wf.source.read_resource": read_resource_node} ), visibility=SourceVisibility(planner=True, client=True, admin_dashboard=True), permissions=SourcePermissions(safe_for_workflow=True, calls_upstream=True), policy=SourcePolicy(platform=True, binding_required=False), description="Platform helpers for explicit source refs.", ) ``` If `local_sources.py` already qualifies specs differently, follow the existing `wf.std` pattern exactly. For MCP-backed servers, ensure `wf.source` is included in the same built-in/platform sources loaded into the broker service. If broker service already imports `builtin_sources()`, no additional change is needed. - [x] **Step 4: Add RPC E2E with fake MCP resource** In `tests/wf_transport_rpc_http/test_mcp_backed_server_rpc.py`, add a test that: 1. Builds an MCP-backed server with a fake adapter/runtime exposing a resource. 2. Creates or saves an artifact using `wf.source.read_resource`. 3. Saves a deployment binding `drive -> demo.personal` but no `wf.source` binding. 4. Runs the deployment with input: ```json { "ref": { "kind": "source_resource_ref", "logical_source": "drive", "uri": "demo://docs/welcome" } } ``` 5. Asserts output text is bounded and the deployment validates runnable. Use existing fake MCP-backed server helpers in that file; do not create a live network dependency. - [x] **Step 5: Run tests and commit** Run: ```bash uv run pytest tests/wf_server/test_local_static_server.py::test_local_static_server_exposes_wf_source_platform_source tests/wf_transport_rpc_http/test_mcp_backed_server_rpc.py -q uv run ruff check src/wf_api/local_sources.py tests/wf_server/test_local_static_server.py tests/wf_transport_rpc_http/test_mcp_backed_server_rpc.py uv run basedpyright --level error src/wf_api/local_sources.py tests/wf_server/test_local_static_server.py tests/wf_transport_rpc_http/test_mcp_backed_server_rpc.py ``` Expected: tests pass, ruff clean, typecheck 0 errors. Commit: ```bash git add src/wf_api/local_sources.py tests/wf_server/test_local_static_server.py tests/wf_transport_rpc_http/test_mcp_backed_server_rpc.py git commit -m "feat: expose wf source resource helper" ``` --- ### Task 6: Docs And Final Verification **Files:** - Modify: `docs/source_provider_guide.md` - Modify: `docs/current_roadmap.md` - [x] **Step 1: Update docs** In `docs/source_provider_guide.md`, add: ````markdown ## Source Resource Refs Resource refs are inert workflow data: ```json { "kind": "source_resource_ref", "logical_source": "drive", "uri": "demo://docs/welcome" } ``` Input/output/state bindings treat this object as ordinary JSON. Only explicit platform helper nodes such as `wf.source.read_resource` dereference it. This keeps large MCP resource payloads out of workflow state unless the workflow asks for them. ```` In `docs/current_roadmap.md`, replace the `wf.source` next-design note with: ```markdown - Completed `wf.source.read_resource`: resource refs are inert pass-by-value data using `logical_source`; explicit platform helper nodes dereference them through runtime/platform context with bounded output. ``` - [x] **Step 2: Final verification** Run: ```bash uv run pytest tests/wf_api/test_source_refs.py tests/wf_api/test_source_helpers.py tests/wf_server/test_local_static_server.py tests/wf_transport_rpc_http/test_mcp_backed_server_rpc.py -q uv run ruff check src/wf_api src/wf_core src/wf_mcp tests/wf_api/test_source_refs.py tests/wf_api/test_source_helpers.py tests/wf_server/test_local_static_server.py tests/wf_transport_rpc_http/test_mcp_backed_server_rpc.py uv run basedpyright --level error src/wf_api src/wf_core src/wf_mcp tests/wf_api/test_source_refs.py tests/wf_api/test_source_helpers.py tests/wf_server/test_local_static_server.py tests/wf_transport_rpc_http/test_mcp_backed_server_rpc.py ``` Expected: focused tests pass, ruff clean, typecheck 0 errors. - [x] **Step 3: Commit docs** ```bash git add docs/source_provider_guide.md docs/current_roadmap.md git commit -m "docs: document source resource refs" ``` --- ## Self-Review - Spec coverage: plan defines pass-by-value refs, keeps core bindings inert, adds platform context, exposes explicit `wf.source.read_resource`, and documents bounded dereferencing. - Placeholder scan: no TBD/TODO/fill-in placeholders remain. - Type consistency: ref field is `logical_source`, helper source is `wf.source`, helper capability is `wf.source.read_resource`.