26 KiB
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.mdfirst. CapabilitySource(policy=SourcePolicy(platform=True, binding_required=False))must exist.wf.stddeployments should validate/run withoutwf.std=wf.stdself-bindings.
File Structure
- Modify
src/wf_core/run_state.py: add optional platform runtime context toRuntimeContext. - 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, andsrc/wf_core/runtime/ops/nodes.py: thread the opaque platform object from workflow execution entrypoints into everyRuntimeContext, including concurrent foreach item execution. - Modify
src/wf_api/runtime_dependencies.pyand server runtime runners: include source binding map/platform context in runtime dependencies. - Create
src/wf_api/source_refs.py: PydanticSourceResourceRefand bounded output model. - Create
src/wf_api/source_helpers.py:wf.source.read_resourceNodeSpec factory. - Modify
src/wf_api/local_sources.pyand MCP server composition to registerwf.source. - Test:
tests/wf_api/test_source_refs.pytests/wf_api/test_source_helpers.pytests/wf_server/test_local_static_server.pytests/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 -
Step 1: Write model tests
Create tests/wf_api/test_source_refs.py:
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")
- Step 2: Run tests and confirm failure
Run:
uv run pytest tests/wf_api/test_source_refs.py -q
Expected: fails because wf_api.source_refs does not exist.
- Step 3: Implement models
Create src/wf_api/source_refs.py:
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.
- Step 4: Run tests and commit
Run:
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:
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.pyor existing nearest runtime context test -
Step 1: Add context tests
Create tests/core/test_runtime_context.py if no equivalent exists:
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
- Step 2: Run test and confirm failure
Run:
uv run pytest tests/core/test_runtime_context.py -q
Expected: fails because RuntimeContext has no platform field.
- Step 3: Add platform field
In src/wf_core/run_state.py, update RuntimeContext:
@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:
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]: ...
- 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.pyexecute_workflow_async(...)execute_workflow_result_async(...)resume_workflow_async(...)resume_workflow_result_async(...)
src/wf_core/runtime/step.pystep_workflow_async(...)_step_async_foreach_item_batch(...)or the nearest helper that callsinvoke_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:
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.
- Step 5: Run tests and commit
Run:
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:
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.pyor nearest existing file -
Step 1: Add tests for source resolution
Create tests/wf_api/test_platform_context.py:
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")
- Step 2: Run tests and confirm failure
Run:
uv run pytest tests/wf_api/test_platform_context.py -q
Expected: fails because SourceBindingPlatformContext does not exist.
- Step 3: Implement platform context
In src/wf_api/platform_context.py, add:
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)
- 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:
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:
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:
read_resource_handler: ReadResourceHandler | None = None
When building the platform context, pass that handler through:
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.
- Step 5: Run tests and commit
Run:
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:
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 -
Step 1: Add helper tests
Create tests/wf_api/test_source_helpers.py:
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"),
)
- Step 2: Run tests and confirm failure
Run:
uv run pytest tests/wf_api/test_source_helpers.py -q
Expected: fails because source_helpers.py does not exist.
- Step 3: Implement bounded helper
Create src/wf_api/source_helpers.py:
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:
from typing import cast
typed_platform = cast(WorkflowPlatformContext, platform)
- Step 4: Add source-uri content access helper
In src/wf_mcp/broker/service/content_access.py, add:
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.
- Step 5: Run tests and commit
Run:
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:
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.pyor source registration seam -
Test:
tests/wf_server/test_local_static_server.py -
Test:
tests/wf_transport_rpc_http/test_mcp_backed_server_rpc.py -
Step 1: Add source inventory test
In tests/wf_server/test_local_static_server.py, add:
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
- Step 2: Run test and confirm failure
Run:
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.
- Step 3: Register source
In src/wf_api/local_sources.py, import:
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:
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:
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.
- 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:
- Builds an MCP-backed server with a fake adapter/runtime exposing a resource.
- Creates or saves an artifact using
wf.source.read_resource. - Saves a deployment binding
drive -> demo.personalbut nowf.sourcebinding. - Runs the deployment with input:
{
"ref": {
"kind": "source_resource_ref",
"logical_source": "drive",
"uri": "demo://docs/welcome"
}
}
- 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.
- Step 5: Run tests and commit
Run:
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:
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 -
Step 1: Update docs
In docs/source_provider_guide.md, add:
## 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:
- 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.
- Step 2: Final verification
Run:
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.
- Step 3: Commit docs
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 iswf.source, helper capability iswf.source.read_resource.