7.5 KiB
WorkflowOperationContext Shape Audit
Date: 2026-06-03
Summary
WorkflowOperationContext is the right seam for protocol-neutral workflow APIs.
After the recent cleanup it no longer depends on MCP transport classes or the old
artifact-cataloger hop. The remaining shape is usable, but it still mixes three
different concepts:
- persistence stores (
artifact_store,draft_workspace_store,run_store) - capability/source lookup (
capability_sources,specs) - operational side effects (
events,runtime,live_sources)
This is acceptable for the current MCP/CLI path. Before building a durable HTTP API or persisted resume workflow, the context should become a little more explicit so future frontends do not inherit accidental MCP-era seams.
Current Shape
@dataclass(frozen=True, slots=True)
class WorkflowOperationContext:
artifact_store: WorkflowArtifactStore | None
draft_workspace_store: DraftWorkspaceStore | None
run_store: RunStore | None
capability_sources: Mapping[str, CapabilitySource]
events: WorkflowEventRecorder
specs: WorkflowSpecProvider
runtime: WorkflowRuntimeRunner
live_sources: WorkflowLiveSourceChecker | None = None
Field Audit
| Field | Used by | Classification | Recommendation |
|---|---|---|---|
artifact_store |
artifacts, deployments, capabilities, runs | Real dependency | Keep, but consider grouping all stores under a WorkflowStores field so store availability is one explicit concern. |
draft_workspace_store |
drafts, artifacts | Real dependency | Keep short-term; same grouping recommendation as artifact_store. |
run_store |
runs | Real dependency | Keep; persisted resume will need this and probably stronger checkpoint APIs. |
capability_sources |
capabilities, deployments, runs, capability requirements | Duplicated dependency | Prefer moving reads through specs.capability_sources; keeping both gives two paths to the same source map. |
events |
artifacts, deployments, operation-context tests | Real dependency | Keep; it is protocol-neutral enough because workflow APIs call record_workflow_event(). |
specs |
drafts, capabilities | Real dependency | Keep; rename to capabilities or source_index later if it grows beyond spec lookup. |
runtime |
capabilities, runs | Real dependency | Keep; this is the important seam for durable runtime backends. |
live_sources |
deployments | Optional adapter hook | Keep optional; only live validation should touch external sources. |
Main Issue
capability_sources and specs.capability_sources are the same concept exposed
twice. Today this is harmless because context_from_service() passes:
specs = WfMcpWorkflowSpecProvider(service)
capability_sources = specs.capability_sources
specs = specs
But it creates a future maintenance trap: one API may iterate
context.capability_sources while another iterates
context.specs.capability_sources. A future backend could accidentally make those
two source maps disagree. context.specs.get_qualified_spec() is a related lookup
operation, but it is not the duplicate inventory path.
Recommendation: remove the top-level capability_sources field in a small future
slice and update consumers to use context.specs.capability_sources.
Store Shape Issue
The context currently stores three optional stores directly. Each domain API has to repeat availability checks:
WorkflowArtifactApi._artifact_store()WorkflowDeploymentApi._artifact_store()WorkflowDraftApi._draft_store()WorkflowRunApi._run_store()
This is fine for MCP where stores may be disabled in test/config paths. For a durable API, store availability is not optional: no run store means no persisted resume.
Recommendation:
- Keep optional stores for current MCP compatibility.
- For durable API work, introduce a required
WorkflowStoresbundle at the API construction boundary, or arequire_stores()helper that produces a stricter context for durable surfaces.
Runtime Shape
WorkflowRuntimeRunner is a good seam:
run_workflow_from_plan()already receives deployment, artifact, and saved subgraph tree.resume_workflow_from_plan()already receives pinned artifact/deployment context.
For persisted resume, this seam should stay. The durable run design should focus on:
- how
RunStorecheckpoints are loaded - how pinned deployment/artifact/source diagnostics are validated
- how interrupted runs are resumed across process restarts
It should not reintroduce direct MCP service access.
Completed Context Simplification
Completed before the persisted-run spec:
- Removed top-level
WorkflowOperationContext.capability_sources. - Updated
wf_apiconsumers to usecontext.specs.capability_sources. - Updated tests that asserted
context.capability_sources. - Kept
WorkflowSpecProvider.capability_sourcesas the single source inventory path.
Why this was first:
- It is low-risk and mechanical.
- It removes duplicate source inventory paths before durable resume depends on source diagnostics.
- It clarifies that source/capability lookup is a single domain dependency.
Inline Cleanup Result
This cleanup was small enough to do inline without a separate agent plan.
-
Updated
src/wf_api/operation_context.py.- Removed
capability_sources: Mapping[str, CapabilitySource]fromWorkflowOperationContext. - Kept
WorkflowSpecProvider.capability_sources.
- Removed
-
Updated
src/wf_mcp/broker/service/workflow_operation_context.py.- Stopped passing
capability_sources=specs.capability_sourcestoWorkflowOperationContext.
- Stopped passing
-
Updated
wf_apiconsumers.- In
capabilities.py, replacedself.context.capability_sourceswithself.context.specs.capability_sources. - In
deployments.py, replacedself.context.capability_sourceswithself.context.specs.capability_sources. - In
runs.py, replacedself.context.capability_sourceswithself.context.specs.capability_sources. - In
capability_requirements.py, replacedcontext.capability_sourceswithcontext.specs.capability_sources.
- In
-
Updated tests.
- In
tests/wf_api/test_operation_context.py, replaced assertions oncontext.capability_sourceswith assertions oncontext.specs.capability_sources.
- In
-
Verification target.
uv run pytest tests/wf_api -quv run pytest tests/wf_mcp/service/test_workflow_runtime.py tests/wf_mcp/workflow_surface -quv run ruff check src/wf_api src/wf_mcp/broker/service/workflow_operation_context.py tests/wf_apiuv run ruff format --check src/wf_api src/wf_mcp/broker/service/workflow_operation_context.py tests/wf_apiuv run basedpyright --level error
Follow-Up For Persisted Runs
After the context simplification, write the persisted run/resume spec around these requirements:
- Run records must store enough pinned environment data to resume after process restart.
- Resume must validate run status/readiness before executing.
- Resume must validate pinned deployment/artifacts/source capabilities before executing.
- Dead external sources should not pause runs; they should produce diagnostics or runtime failure. Only workflow interrupts pause/resume.
- Trace output should stay paged/ranged; full trace remains opt-in.
Non-Goals
- Do not move stores out of
wf_artifactsin this pass. - Do not make
live_sourcesrequired; live checks are optional and expensive. - Do not make
WorkflowOperationContextdepend onwf_mcp. - Do not redesign
WorkflowRuntimeRunneruntil the persisted-run spec needs a concrete change.