first slice: operation context scaffolding

splitting WorkflowSurfaceHandlers (because it is BIG)
This commit is contained in:
lda
2026-06-01 23:28:11 +07:00 Verified
parent 7ada524787
commit 6fcd6b7593
5 changed files with 395 additions and 0 deletions
@@ -0,0 +1,106 @@
from __future__ import annotations
from dataclasses import dataclass
from typing import Any
from wf_api.operation_context import (
WorkflowArtifactCataloger,
WorkflowEventRecorder,
WorkflowLiveSourceChecker,
WorkflowOperationContext,
WorkflowRuntimeRunner,
WorkflowSpecProvider,
)
from .core import WfMcpService
@dataclass(frozen=True, slots=True)
class WfMcpWorkflowEventRecorder(WorkflowEventRecorder):
"""Adapter-owned event recorder backed by WfMcpService."""
service: WfMcpService
def record_event(self, event: Any) -> None:
self.service._record_event(event) # noqa: SLF001
@dataclass(frozen=True, slots=True)
class WfMcpWorkflowSpecProvider(WorkflowSpecProvider):
"""Adapter-owned spec provider backed by WfMcpService."""
service: WfMcpService
@property
def capability_sources(self):
return self.service.capability_sources
def get_qualified_spec(self, qualified_name: str) -> object:
return self.service._get_qualified_spec(qualified_name) # noqa: SLF001
@dataclass(frozen=True, slots=True)
class WfMcpWorkflowArtifactCataloger(WorkflowArtifactCataloger):
"""Adapter-owned artifact catalog formatter backed by WfMcpService."""
service: WfMcpService
def workflow_artifact_catalog_entry(self, artifact):
return self.service.workflow_artifact_catalog_entry(artifact)
@dataclass(frozen=True, slots=True)
class WfMcpWorkflowRuntimeRunner(WorkflowRuntimeRunner):
"""Adapter-owned runtime runner backed by WfMcpService."""
service: WfMcpService
async def run_workflow_from_plan(self, plan, **kwargs):
return await self.service.run_workflow_from_plan(plan, **kwargs)
async def resume_workflow_from_plan(self, plan, **kwargs):
return await self.service.resume_workflow_from_plan(plan, **kwargs)
@dataclass(frozen=True, slots=True)
class WfMcpWorkflowLiveSourceChecker(WorkflowLiveSourceChecker):
"""Placeholder live source checker while Slice 4A only defines the seam.
See `docs/superpowers/plans/2026-06-01-wf-api-extraction-roadmap.md`.
Real live source checks still live in workflow handlers until a later
capability-domain extraction can move them without dragging MCP adapters
into `wf_api`.
"""
service: WfMcpService
async def available_sources(self) -> list[object]:
# Existing live source availability logic still lives near handlers.
# Slice 4A only creates the seam; it does not move live-check behavior.
return []
def context_from_service(service: WfMcpService) -> WorkflowOperationContext:
"""Adapt the current MCP service stack into a protocol-neutral context."""
specs = WfMcpWorkflowSpecProvider(service)
return WorkflowOperationContext(
artifact_store=service.artifact_store,
draft_workspace_store=service.draft_workspace_store,
run_store=service.run_store,
capability_sources=specs.capability_sources,
events=WfMcpWorkflowEventRecorder(service),
specs=specs,
artifacts=WfMcpWorkflowArtifactCataloger(service),
runtime=WfMcpWorkflowRuntimeRunner(service),
live_sources=WfMcpWorkflowLiveSourceChecker(service),
)
__all__ = [
"WfMcpWorkflowArtifactCataloger",
"WfMcpWorkflowEventRecorder",
"WfMcpWorkflowLiveSourceChecker",
"WfMcpWorkflowRuntimeRunner",
"WfMcpWorkflowSpecProvider",
"context_from_service",
]