docs/misc

This commit is contained in:
lda
2026-06-01 17:14:28 +07:00 Verified
parent bd190a5547
commit d05da78847
7 changed files with 4218 additions and 5 deletions
@@ -0,0 +1,359 @@
# wf_api Extraction Roadmap
> **For agentic workers:** This is an architecture roadmap, not a deterministic implementation checklist. Use it to choose and scope future focused implementation plans. Do not execute multiple slices at once.
**Goal:** Extract a protocol-neutral workflow application API from `wf_mcp` while preserving current process-local behavior and avoiding a large semantic rewrite.
**Architecture:** `wf_api` becomes the long-lived in-process application service layer. `wf_mcp`, `wf_cli`, and future HTTP/UI adapters call `wf_api`; `wf_api` must not import `wf_mcp`.
**Current Constraint:** `WorkflowSurfaceHandlers` is large and currently depends on `WfMcpService`. The first slice fixes dependency direction only; later slices can split and rename once the boundary is correct.
---
## Target Shape
```text
wf_core = workflow execution kernel
wf_authoring = Python authoring sugar and NodeSpec construction
wf_artifacts = saved workflow/deployment/run models and stores
wf_platform = source/capability/event platform primitives
wf_api = process-local workflow application service/use cases
wf_mcp = MCP adapter and MCP runtime/proxy/admin surface
wf_cli = CLI adapter
```
Desired dependency direction:
```text
wf_cli ─┐
├──> wf_api ───> wf_artifacts / wf_platform / wf_core / wf_authoring
wf_mcp ─┘
```
Forbidden dependency direction:
```text
wf_api -> wf_mcp
```
Process-local behavior remains the default:
```python
config = load_broker_config(path)
service = build_service_from_config(config)
api = WorkflowApi(WfMcpWorkflowApiBackend(service))
```
No FastAPI, daemon, socket, auth redesign, or network boundary is required for this extraction.
## Slice 1: Dependency Direction Cleanup
### Goal
Make both CLI and MCP call a protocol-neutral `WorkflowApi`, while `WorkflowApi`
does not import `wf_mcp`.
### Allowed
- Create `src/wf_api/`.
- Add `WorkflowApiBackend` protocol.
- Add `WorkflowApi`.
- Add `WfMcpWorkflowApiBackend` adapter around `WfMcpService`.
- Update `wf_cli.context` to construct `WorkflowApi`.
- Update MCP workflow tool registration to use `WorkflowApi`.
- Keep compatibility aliases if existing imports need a transition.
- Keep payloads and behavior unchanged.
### Not Allowed
- Do not split `WorkflowApi` by domain yet.
- Do not move every helper module yet.
- Do not rename events yet.
- Do not redesign stores.
- Do not add FastAPI.
- Do not change response payloads.
- Do not change command/tool names.
### Shape
```text
src/wf_api/
__init__.py
backend.py # WorkflowApiBackend protocol
service.py # WorkflowApi, initially large
src/wf_mcp/workflow_surface/
handlers.py # compatibility alias or thin import shim
tools.py # MCP adapter; calls WorkflowApi
src/wf_mcp/broker/service/
workflow_api_backend.py # WfMcpWorkflowApiBackend
```
`WorkflowApiBackend` should expose only what `WorkflowApi` currently uses:
```python
artifact_store
draft_workspace_store
run_store
capability_sources
get_qualified_spec(...)
record_event(...)
run_workflow_from_plan(...)
resume_workflow_from_plan(...)
workflow_artifact_catalog_entry(...)
```
Live source validation is the awkward part because it touches MCP connections,
adapters, and auth. In Slice 1, it can stay behind backend methods/callbacks
without making `wf_api` import `wf_mcp`.
### Success Criteria
- `wf_api` imports no `wf_mcp` modules.
- `wf_cli` uses `WorkflowApi`.
- `wf_mcp.workflow_surface.tools` uses `WorkflowApi`.
- Existing MCP workflow-surface tests pass.
- Existing CLI tests pass.
- Behavior and payloads are unchanged.
## Slice 2: Stabilize API Names And Compatibility Shims
### Goal
Make naming honest without breaking callers.
### Likely Work
- Rename `WorkflowSurfaceHandlers` usage to `WorkflowApi` in tests and CLI code.
- Keep a temporary import shim:
```python
from wf_api import WorkflowApi as WorkflowSurfaceHandlers
```
- Update docs to say:
```text
MCP tools and CLI commands are adapters over wf_api.
```
- Rename test fixture helpers from `handlers(...)` to `api(...)` if useful.
### If/Then
- If shims create confusion, remove them quickly after imports are migrated.
- If too many downstream imports still expect `WorkflowSurfaceHandlers`, keep the shim for one release/work session and document it as deprecated.
### Success Criteria
- New code imports `WorkflowApi`.
- Old name remains only in compatibility modules or is gone.
- Docs describe `wf_api` as the application service layer.
## Slice 3: Move Protocol-Neutral Workflow Surface Modules
### Goal
Move helper modules that are not MCP-specific out of `wf_mcp.workflow_surface`.
### Candidate Moves
```text
wf_mcp.workflow_surface.models -> wf_api.models
wf_mcp.workflow_surface.next_actions -> wf_api.next_actions
wf_mcp.workflow_surface.wrapper_hints -> wf_api.wrapper_hints
wf_mcp.workflow_surface.refs -> wf_api.refs
wf_mcp.workflow_surface.run_lifecycle -> wf_api.run_lifecycle
wf_mcp.workflow_surface.runtime_dependencies -> wf_api.runtime_dependencies
wf_mcp.workflow_surface.saved_subgraphs -> wf_api.saved_subgraphs
wf_mcp.workflow_surface.constants -> wf_api.constants
```
### If/Then
- If a module imports MCP connection/adapters/auth, do not move it in this slice.
- If imports become circular, leave a shim in the old location and move one module at a time.
- If a helper is really platform vocabulary, consider `wf_platform` instead of `wf_api`.
### Success Criteria
- `wf_api` owns workflow API helper models.
- `wf_mcp.workflow_surface` keeps only MCP adapter/shim code.
- Tests still pass with import-only or near-import-only changes.
## Slice 4: Split The Big API By Domain
### Goal
Reduce the large `WorkflowApi` class after the package boundary is correct.
### Candidate Shape
```text
src/wf_api/
service.py # facade that composes domain services
capabilities.py
drafts.py
artifacts.py
deployments.py
runs.py
```
Possible facade:
```python
class WorkflowApi:
capabilities: CapabilityApi
drafts: DraftApi
artifacts: ArtifactApi
deployments: DeploymentApi
runs: RunApi
```
Compatibility can keep flat methods:
```python
async def list_capabilities(...):
return await self.capabilities.list_capabilities(...)
```
### If/Then
- If callers benefit from flat methods, keep the facade flat and split internals only.
- If domain APIs are clean enough, expose nested services later.
- If a method spans domains, keep it in the facade until a better boundary appears.
### Success Criteria
- Each domain file is readable on its own.
- Public payloads remain unchanged.
- `wf_cli` and `wf_mcp` do not care about the internal split.
## Slice 5: Move Listing/Event Primitives To Better Homes
### Goal
Remove remaining protocol-neutral utilities from MCP-named packages.
### Candidate Moves
```text
wf_mcp.shared.listing.matches_query -> wf_platform.listing or wf_api.listing
wf_mcp.shared.listing.paged_list_payload -> wf_platform.listing or wf_api.listing
wf_mcp.events.McpEvent -> wf_platform.events.DomainEvent
wf_mcp.events.EventBus -> wf_platform.events.EventBus
```
### If/Then
- If events are only used by MCP admin/proxy code, leave them in `wf_mcp`.
- If events describe artifact/deployment/run lifecycle, move or fork them into `wf_platform`.
- If renaming `McpEvent` causes churn, introduce `DomainEvent` first and keep `McpEvent` as an alias temporarily.
### Success Criteria
- Workflow API lifecycle events no longer require MCP naming.
- Listing helpers used by CLI/API are not imported from `wf_mcp`.
## Slice 6: Store Construction And Config Boundary
### Goal
Make store/runtime construction less MCP-owned.
### Current Problem
`build_service_from_config` and `WfMcpService.__post_init__` currently build or own protocol-neutral stores. CLI uses this path because it is pragmatic, but long term config/store construction should be reusable without MCP server assumptions.
### Candidate Work
- Extract config-to-store construction into a neutral builder.
- Keep MCP connection config in `wf_mcp`.
- Let `wf_cli` and future API/server adapters reuse the neutral store builder.
### If/Then
- If extraction creates too many config model moves, defer it.
- If CLI needs only current config behavior, keep using `wf_mcp` builder until FastAPI/UI pressure appears.
### Success Criteria
- Artifact/draft/run stores can be constructed without starting MCP concepts.
- Current `wf_mcp.config.json` remains supported.
## Slice 7: Future HTTP/FastAPI Adapter
### Goal
Expose `WorkflowApi` over HTTP only after the in-process API is stable.
### Not Yet
Do not start this until:
- `wf_api` exists.
- CLI and MCP both call `wf_api`.
- Run/draft/deployment payloads are stable enough.
- Auth and multi-client lifecycle questions are explicit.
### Future Shape
```text
wf_http or wf_server
routes/
capabilities.py
drafts.py
artifacts.py
deployments.py
runs.py
```
Routes should be thin:
```python
@router.post("/runs")
async def start_run(...):
return await api.run_deployment(...)
```
### Success Criteria
- HTTP is an adapter, not a new source of workflow logic.
- Process-local API remains usable without HTTP.
## Open Questions
1. Should `WorkflowApiBackend` be one protocol or several domain protocols?
- Recommendation for Slice 1: one protocol. Split later only if it hurts.
2. Should live source validation live in `wf_api`?
- Recommendation: `wf_api` owns the operation, backend owns the live-check implementation.
3. Should `wf_api` expose flat methods or nested services?
- Recommendation for Slice 1: flat methods for compatibility. Consider nested internals later.
4. Should `WorkflowSurfaceHandlers` disappear immediately?
- Recommendation: no. Keep a short-lived shim if it reduces churn.
5. Should `wf_cli` stop importing `wf_mcp` after Slice 1?
- Not fully. It may still use `wf_mcp` config/service construction until store/config extraction happens.
## Immediate Next Plan
Write a focused implementation plan for Slice 1 only:
```text
docs/superpowers/plans/YYYY-MM-DD-wf-api-slice-1-dependency-direction.md
```
That plan should be code-level and deterministic:
- add `wf_api.backend`
- add `wf_api.service`
- add `WfMcpWorkflowApiBackend`
- update CLI/MCP construction
- keep compatibility import
- run focused MCP workflow-surface and CLI tests
Do not include Slice 2+ work in that implementation plan.
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,467 @@
# wf_api Extraction Map
## Executive Summary
`WorkflowSurfaceHandlers` (`src/wf_mcp/workflow_surface/handlers.py:104`) is a 1,130-line application-service class that owns all workflow-facing operations: capability discovery, artifact CRUD, draft workspace management, deployment validation, and durable run lifecycle. It currently depends on `WfMcpService` (`src/wf_mcp/broker/service/core.py:79`) via a single `self.service` field, but only uses a narrow slice of that 960-line god object.
The extraction seam is clean: `WorkflowSurfaceHandlers` touches ~6 distinct capabilities of `WfMcpService` (artifact store, draft store, run store, capability sources, workflow execution, event bus). None of these require MCP transport, connection adapters, or proxy machinery. The class is already used protocol-neutrally by `wf_cli` via `CliContext` (`src/wf_cli/context.py:14`).
**Recommended first slice:** Option B — introduce a `WorkflowApi` facade backed by a small `WorkflowApiBackend` ports object, adapt `WfMcpService` into it, then move the facade to `wf_api`. This avoids the large-class move (Option A) and the premature domain split (Option C).
## Public Operation Inventory
### WorkflowSurfaceHandlers — Public Methods
| Method | Line | Category | Input Parameters | Return Shape | Direct Dependencies | Callers/Tests |
|--------|------|----------|-----------------|--------------|--------------------|--------------------|
| `list_artifacts` | 110 | artifacts | `query`, `kind`, `cursor`, `limit` | `{nodes, next_cursor, total}` | `self.service.artifact_store`, `self.service.workflow_artifact_catalog_entry` | `tools.py:49`, `artifacts.py:41`, `test_artifacts.py:11` |
| `list_capabilities` | 147 | capabilities | `query`, `source_id`, `cursor`, `limit` | `{capabilities, next_cursor, total}` | `self.service.capability_sources` | `tools.py:91`, `caps.py:41`, `test_capabilities.py:20` |
| `inspect_capability` | 193 | capabilities | `qualified_name` | capability contract dict | `self.service.capability_sources` | `tools.py:132`, `caps.py:65`, `test_capabilities.py:102` |
| `call_capability` | 213 | capabilities | `qualified_name`, `payload`, `deployment_id` | `{qualified_name, source_id, kind, deployment_id, outcome, output, diagnostics}` | `self.service._get_qualified_spec`, `self.service.capability_sources`, `self.service.run_workflow_from_plan`, `self.service.artifact_store` | `tools.py:144`, `test_capabilities.py:50`, `test_wrappers.py:69` |
| `save_artifact` | 416 | artifacts | `artifact` dict | `{artifact_id, version, saved}` | `self.service.artifact_store`, `self.service._record_event` | `tools.py:162` |
| `create_artifact_from_plan` | 437 | artifacts | `artifact_id`, `version`, `title`, `plan`, `outcomes`, `kind`, `description`, `required_capabilities`, `source_bindings`, `created_from_catalog_version` | `{artifact_id, version, saved}` | `self.service.artifact_store`, `self.service._record_event` | `tools.py:186`, `test_wrappers.py:15` |
| `validate_draft` | 492 | drafts | `draft` dict | `{status, diagnostics, compiled_plan}` | (none — pure wf_artifacts) | `tools.py:170`, `test_drafts.py:23` |
| `compile_draft` | 498 | drafts | `draft` dict | `{compiled_plan, required_capabilities}` | `self.service` (via `_required_capabilities_for_plan`) | `tools.py:178` |
| `create_artifact_from_draft` | 511 | drafts/artifacts | `artifact_id`, `version`, `title`, `draft`, `outcomes`, `kind`, `description`, `required_capabilities`, `source_bindings`, `created_from_catalog_version` | `{artifact_id, version, saved, required_logical_sources, suggested_bindings}` | `self.service.artifact_store`, `self.service._record_event` | `tools.py:229`, `test_drafts.py:61` |
| `patch_draft` | 570 | drafts | `draft`, `patch` | patched draft dict | (none — pure wf_artifacts) | `tools.py:269` |
| `list_draft_workspaces` | 583 | draft workspaces | (none) | `{workspaces}` | `self.service.draft_workspace_store` | `tools.py:280`, `test_drafts.py` |
| `create_draft_workspace` | 593 | draft workspaces | `workspace_id`, `draft`, `title` | workspace summary | `self.service.draft_workspace_store` | `tools.py:293`, `test_drafts.py` |
| `get_draft_workspace` | 607 | draft workspaces | `workspace_id`, `include_draft` | workspace summary | `self.service.draft_workspace_store` | `tools.py:309`, `test_drafts.py` |
| `delete_draft_workspace` | 619 | draft workspaces | `workspace_id` | `{workspace_id, deleted, status}` | `self.service.draft_workspace_store` | `tools.py:325`, `test_drafts.py` |
| `validate_draft_workspace` | 627 | draft workspaces | `workspace_id` | workspace summary | `self.service.draft_workspace_store` | `tools.py:360`, `test_drafts.py` |
| `patch_draft_workspace` | 641 | draft workspaces | `workspace_id`, `revision`, `patch` | workspace summary | `self.service.draft_workspace_store` | `tools.py:341`, `test_drafts.py` |
| `set_draft_name` | 655 | draft workspaces | `workspace_id`, `revision`, `name` | workspace summary | (delegates to `patch_draft_workspace`) | `tools.py:372`, `test_drafts.py` |
| `set_draft_route` | 668 | draft workspaces | `workspace_id`, `revision`, `step_id`, `outcome`, `target` | workspace summary | (delegates to `patch_draft_workspace`) | `tools.py:386`, `test_drafts.py` |
| `set_step_input_map` | 692 | draft workspaces | `workspace_id`, `revision`, `step_id`, `input_map` | workspace summary | (delegates to `patch_draft_workspace`) | `tools.py:405`, `test_drafts.py` |
| `set_step_output_map` | 712 | draft workspaces | `workspace_id`, `revision`, `step_id`, `output_map` | workspace summary | (delegates to `patch_draft_workspace`) | `tools.py:425`, `test_drafts.py` |
| `create_minimal_draft_workspace` | 732 | draft workspaces | `workspace_id`, `name`, `capability_name`, schemas, bindings | workspace summary | `self.service.draft_workspace_store` (via `_draft_store`), `self.service._get_qualified_spec` (via `_outcomes_for_capability`) | `tools.py:446`, `test_drafts.py` |
| `create_draft_workspace_from_capability` | 802 | draft workspaces | `workspace_id`, `capability_name`, optional schemas/bindings | workspace summary + wrapper_hints + next_actions | (delegates to `inspect_capability` + `create_minimal_draft_workspace`) | `tools.py:474`, `test_drafts.py` |
| `create_artifact_from_workspace` | 847 | artifacts | `workspace_id`, `artifact_id`, `version`, `title`, `outcomes`, `kind`, `description`, `required_capabilities`, `source_bindings`, `created_from_catalog_version` | save result or validation error | `self.service.draft_workspace_store` | `tools.py:502`, `test_drafts.py` |
| `create_wrapper_from_workspace` | 884 | artifacts | same as above (without `kind`) | save result | (delegates to `create_artifact_from_workspace` with `kind="wrapper"`) | `tools.py:536`, `test_drafts.py` |
| `inspect_artifact` | 917 | artifacts | `artifact_id`, `version` | full artifact dict | `self.service.artifact_store` | `tools.py:563`, `test_artifacts.py:58` |
| `list_deployments` | 925 | deployments | (none) | `{deployments}` | `self.service.artifact_store` | `tools.py:577`, `test_deployments.py` |
| `inspect_deployment` | 935 | deployments | `deployment_id` | full deployment dict | `self.service.artifact_store` | `tools.py:585`, `test_deployments.py` |
| `save_deployment` | 942 | deployments | `deployment` dict | `{deployment_id, artifact_id, artifact_version, saved}` | `self.service.artifact_store`, `self.service._record_event` | `tools.py:593`, `test_deployments.py` |
| `delete_deployment` | 965 | deployments | `deployment_id` | `{deployment_id, deleted}` | `self.service.artifact_store`, `self.service._record_event` | `tools.py:604`, `test_deployments.py` |
| `validate_deployment` | 979 | deployments | `deployment_id`, `live_check` | `{deployment_id, artifact_id, artifact_version, status, diagnostics, next_actions}` | `self.service.artifact_store`, `self.service.capability_sources`, `self.service.connections`, `self.service.adapters`, `self.service.load_auth` | `tools.py:617`, `test_deployments.py:39` |
| `run_deployment` | 1010 | runs | `deployment_id`, `workflow_input`, `trace_range` | run payload dict | `self.service.artifact_store`, `self.service.run_workflow_from_plan`, `self.service.run_store` | `tools.py:650`, `test_runs.py:40` |
| `resume_run` | 1074 | runs | `run_id`, `resume_payload`, `resume_outcome`, `trace_range` | run payload dict | `self.service.run_store`, `self.service.resume_workflow_from_plan`, `self.service.capability_sources` | `tools.py:681`, `test_runs.py` |
| `inspect_run` | 1154 | runs | `run_id` | run payload dict | `self.service.run_store` | `tools.py:712`, `test_runs.py:78` |
| `read_run_trace` | 1172 | runs | `run_id`, `trace_range` | run payload dict with trace | `self.service.run_store` | `tools.py:722`, `test_runs.py:79` |
### Private Methods
| Method | Line | Notes |
|--------|------|-------|
| `_wrapper_artifact_for_capability_name` | 272 | helper for wrapper resolution |
| `_wrapper_capability_summaries` | 297 | projects wrappers into capability discovery |
| `_wrapper_capability_detail` | 340 | NodeSpec-like contract for wrappers |
| `_call_wrapper_artifact` | 371 | executes wrapper through workflow runner |
| `_draft_store` | 578 | accessor for `self.service.draft_workspace_store` |
| `_outcomes_for_capability` | 911 | delegates to `self.service._get_qualified_spec` |
| `_run_store` | 1196 | accessor for `self.service.run_store` |
| `_deployment_validation` | 1202 | shared validation logic for deployment operations |
### Module-Level Private Helpers
| Function | Line | Used By | Dependencies |
|----------|------|---------|-------------|
| `_available_sources` | 1238 | `_deployment_validation`, `resume_run`, `_live_source_diagnostics` | `service.capability_sources` |
| `_live_source_diagnostics` | 1277 | `validate_deployment` | `service.connections`, `service.adapters`, `service.load_auth` |
| `_required_live_sources` | 1326 | `_live_source_diagnostics` | (pure) |
| `_required_capabilities_for_plan` | 1341 | `compile_draft` | `service` (for `_observed_node_specs`) |
| `_required_capability_payloads` | 1373 | multiple | (pure) |
| `_suggested_self_bindings` | 1382 | `create_artifact_from_draft` | (pure) |
| `_observed_node_specs` | 1389 | `create_artifact_from_plan`, `create_artifact_from_draft`, `_required_capabilities_for_plan` | `service.capability_sources` |
| `_schema_field_names` | 1400 | `list_capabilities` | (pure) |
| `_draft_input_maps` | 1408 | `create_minimal_draft_workspace` | (pure) |
| `_draft_output_map` | 1436 | `create_minimal_draft_workspace` | (pure) |
| `_draft_input_bindings_payload` | 1449 | `create_minimal_draft_workspace`, `set_step_input_map` | (pure) |
| `_draft_output_bindings_payload` | 1463 | `create_minimal_draft_workspace`, `set_step_output_map` | (pure) |
| `_graph_path_payload` | 1471 | multiple | (pure) |
| `_local_path_payload` | 1476 | multiple | (pure) |
| `_state_path_payload` | 1480 | multiple | (pure) |
| `_escape_json_pointer` | 1484 | draft patch helpers | (pure) |
| `_draft_name_from_capability` | 1489 | `create_draft_workspace_from_capability` | (pure) |
| `_source_id_for_capability` | 1494 | `call_capability` | (pure) |
| `_capability_name` | 1505 | `_available_sources` | (pure) |
| `_artifact_capability_id` | 1516 | multiple | (pure) |
| `_raw_plan_from_artifact` | 1526 | `run_deployment`, `resume_run`, `_call_wrapper_artifact` | (pure) |
| `_plan_field` | 1543 | `_raw_plan_from_artifact` | (pure) |
| `_plan_nodes` | 1553 | `_required_capabilities_for_plan` | (pure) |
| `_run_payload` | 1558 | `run_deployment`, `resume_run`, `inspect_run`, `read_run_trace` | (pure) |
| `_interrupt_payload` | 1608 | `_run_payload` | (pure) |
| `_deployment_summary` | 1621 | `list_deployments` | (pure) |
## WfMcpService Dependency Inventory
### Members Accessed from WorkflowSurfaceHandlers
| Member | Type | Accessed Via | Usage Count | Category |
|--------|------|-------------|-------------|----------|
| `artifact_store` | `WorkflowArtifactStore \| None` | `self.service.artifact_store` | ~25 | artifact storage |
| `draft_workspace_store` | `DraftWorkspaceStore \| None` | `self.service.draft_workspace_store` | ~8 | draft storage |
| `run_store` | `RunStore \| None` | `self.service.run_store` | ~5 | run storage |
| `capability_sources` | `dict[str, CapabilitySource]` | `self.service.capability_sources` | ~8 | source/capability inventory |
| `_get_qualified_spec(qualified_name)` | `NodeSpec` | `self.service._get_qualified_spec(...)` | 3 | **private helper** |
| `_record_event(event)` | `None` | `self.service._record_event(...)` | 7 | **private helper** (event bus) |
| `run_workflow_from_plan(...)` | `RunState` | `self.service.run_workflow_from_plan(...)` | 3 | workflow execution |
| `resume_workflow_from_plan(...)` | `RunState` | `self.service.resume_workflow_from_plan(...)` | 1 | workflow execution |
| `workflow_artifact_catalog_entry(artifact)` | `WorkflowArtifactCatalogEntry` | `self.service.workflow_artifact_catalog_entry(...)` | 1 | artifact projection |
| `connections` | `ConnectionRegistry` | `self.service.connections` | 1 | **MCP-specific** (live source check) |
| `adapters` | `dict[str, BackendAdapter]` | `self.service.adapters` | 1 | **MCP-specific** (live source check) |
| `load_auth(connection_id)` | `AuthRecord \| None` | `self.service.load_auth(...)` | 1 | **MCP-specific** (live source check) |
### WfMcpService Members NOT Used by WorkflowSurfaceHandlers
These members of `WfMcpService` (`src/wf_mcp/broker/service/core.py`) are NOT accessed by `WorkflowSurfaceHandlers`:
- `store` (MCP catalog/auth storage)
- `default_catalog_max_age_seconds`
- `include_builtin_specs`
- `tool_executor`
- `event_bus` (only accessed indirectly via `_record_event`)
- `register_connection`
- `sync_connections_from_config`
- `register_adapter`
- `_tool_executor_for`
- `save_auth`
- `register_specs`
- `get_catalog`
- `get_planner_catalog`
- `list_sources`
- `list_source_summaries`
- `inspect_source`
- `list_available_specs`
- `get_connection_snapshot`
- `connection_statuses`
- `list_resources`
- `list_prompts`
- `get_resource`
- `get_prompt`
- `read_resource`
- `invoke_method`
- `send_notification`
- `render_prompt`
- `_local_documentation_resource`
- `_local_documentation_prompt`
- `refresh_connection_catalog`
- `compile_plan`
- `_prepare_workflow_runtime`
- `register_capability_source`
- `_hydrate_connection_source_from_snapshot`
- `_spec_from_snapshot_entry`
- `_record_catalog_change_events`
## Protocol-Neutral vs MCP-Specific Dependencies
| Dependency | Package Today | MCP-Specific? | Should Live In |
|-----------|---------------|---------------|----------------|
| `WorkflowArtifactStore` | `wf_artifacts` | No | `wf_artifacts` (already there) |
| `DraftWorkspaceStore` | `wf_artifacts` | No | `wf_artifacts` (already there) |
| `RunStore` | `wf_artifacts` | No | `wf_artifacts` (already there) |
| `CapabilitySource` | `wf_platform` | No | `wf_platform` (already there) |
| `EventBus` / `McpEvent` | `wf_mcp.events` | **Renamed** but protocol-neutral | `wf_api.events` or rename to `wf_platform.events` |
| `make_event` | `wf_mcp.events` | Protocol-neutral | Move to `wf_api` or `wf_platform` |
| `matches_query` | `wf_mcp.shared.listing` | Protocol-neutral | `wf_api.shared` or `wf_platform` |
| `paged_list_payload` | `wf_mcp.shared.listing` | Protocol-neutral | `wf_api.shared` or `wf_platform` |
| `page_items` | `wf_platform` | No | `wf_platform` (already there) |
| `RawWorkflowPlan` | `wf_mcp.models` | No | `wf_api.models` or `wf_core` |
| `ConnectionRegistry` | `wf_mcp.connections` | **Yes** (MCP connection model) | stays in `wf_mcp` |
| `ConnectionConfig` | `wf_mcp.models` | **Yes** (MCP connection model) | stays in `wf_mcp` |
| `BackendAdapter` | `wf_mcp.sdk` | **Yes** (MCP adapter protocol) | stays in `wf_mcp` |
| `require_adapter` | `wf_mcp.broker.service.adapters` | **Yes** (MCP adapter lookup) | stays in `wf_mcp` |
| `AuthRecord` | `wf_mcp.models` | **Yes** (MCP auth model) | stays in `wf_mcp` |
| `Store` / `FileStore` | `wf_mcp.storage` | **Yes** (MCP catalog/auth storage) | stays in `wf_mcp` |
| `WorkflowArtifact`, `WorkflowDeployment`, etc. | `wf_artifacts` | No | `wf_artifacts` (already there) |
| `CapabilityRef`, `CapabilitySource` | `wf_platform` | No | `wf_platform` (already there) |
| `NodeSpec`, `build_async_registry` | `wf_authoring` | No | `wf_authoring` (already there) |
| `RuntimeContext` | `wf_core` | No | `wf_core` (already there) |
| `InputBinding`, `OutputBinding` | `wf_core.models.steps` | No | `wf_core` (already there) |
| `GraphSourcePath`, `LocalPath`, `StatePath` | `wf_core.paths` | No | `wf_core` (already there) |
| `TraceRange` | `wf_mcp.workflow_surface.models` | No | `wf_api.models` |
| `NextActions` | `wf_mcp.workflow_surface.next_actions` | No | `wf_api.next_actions` |
| `SavedSubgraphTree` | `wf_mcp.workflow_surface.saved_subgraphs` | No | `wf_api.saved_subgraphs` |
| `resolve_runtime_dependencies` | `wf_mcp.workflow_surface.runtime_dependencies` | No | `wf_api.runtime_dependencies` |
| `wrapper_hints_for_capability` | `wf_mcp.workflow_surface.wrapper_hints` | No | `wf_api.wrapper_hints` |
| `parse_workflow_surface_capability_id` | `wf_mcp.workflow_surface.refs` | No | `wf_api.refs` |
| `run_lifecycle` helpers | `wf_mcp.workflow_surface.run_lifecycle` | No | `wf_api.run_lifecycle` |
| `constants` | `wf_mcp.workflow_surface.constants` | No | `wf_api.constants` |
| `create_pinned_environment` | `wf_mcp.workflow_surface.run_lifecycle` | No | `wf_api.run_lifecycle` |
### Summary Classification
**Protocol-neutral (should move to `wf_api`):**
- All `wf_artifacts` types and functions
- All `wf_platform` types
- All `wf_authoring` types
- All `wf_core` types
- `EventBus` / `McpEvent` / `make_event` (rename from "Mcp" to generic)
- `matches_query`, `paged_list_payload` (listing utilities)
- `RawWorkflowPlan`
- `TraceRange`, `NextActions`, `SavedSubgraphTree`, `resolve_runtime_dependencies`, `wrapper_hints`, `refs`, `run_lifecycle`, `constants`
**MCP-specific (stays in `wf_mcp`):**
- `ConnectionRegistry`, `ConnectionConfig`
- `BackendAdapter`, `require_adapter`
- `AuthRecord`
- `Store`, `FileStore` (MCP catalog/auth persistence)
- `BrokerConfig`, `build_service_from_config`
- All proxy/MCP transport/session code
- `FastMCP` tool registration (`register_workflow_tools`)
## Extraction Options
### Option A: Move Class First
Move `WorkflowSurfaceHandlers` to `wf_api.service.WorkflowApi`, keep same constructor accepting `WfMcpService`.
**Pros:**
- Minimal code changes — just move the file, update imports
- Tests keep passing with import-only changes
- No new abstractions
**Cons:**
- `wf_api` would still depend on `WfMcpService` (a `wf_mcp` type)
- Circular dependency risk: `wf_api``wf_mcp``wf_api` (if `wf_mcp` tools import from `wf_api`)
- Doesn't actually decouple from MCP — just relocates the code
- Makes future FastAPI adapter harder because the service dependency is still monolithic
### Option B: Introduce API Facade with Ports (Recommended)
Create `WorkflowApi` that depends on a smaller `WorkflowApiBackend`/ports object instead of all `WfMcpService`, then adapt `WfMcpService` into that backend.
**Pros:**
- Clean dependency direction: `wf_api` → ports/interfaces, `wf_mcp` → adapts `WfMcpService` into ports
- `wf_cli` can also adapt its own backend (or reuse the `wf_mcp` adapter)
- FastAPI later becomes just another adapter over the same ports
- Testable in isolation — mock the ports
- Incremental: can introduce ports one domain at a time
**Cons:**
- More upfront work — need to define the port interface
- Risk of over-abstracting if ports are too fine-grained
- Need to decide what `WorkflowApiBackend` actually exposes
### Option C: Split Handlers by Domain First
Split capabilities/drafts/artifacts/deployments/runs into separate classes before moving packages.
**Pros:**
- Each domain class is smaller and easier to reason about
- Could enable partial extraction (move artifacts first, then drafts, etc.)
- Better separation of concerns regardless of extraction
**Cons:**
- Large refactor with many test changes
- Still coupled to `WfMcpService` until ports are introduced
- May create artificial boundaries — some operations span domains (e.g., `create_artifact_from_workspace` touches both drafts and artifacts)
- Doesn't solve the packaging problem by itself
## Recommended First Slice
**Option B: Introduce API facade with ports, starting with a minimal port surface.**
### Why
1. The dependency direction is the core problem. `WorkflowSurfaceHandlers` currently reaches into `WfMcpService` for ~6 distinct capabilities. Extracting without ports just moves the coupling.
2. The port surface is small. Looking at the actual accesses, the port interface needs:
- `artifact_store: WorkflowArtifactStore | None`
- `draft_workspace_store: DraftWorkspaceStore | None`
- `run_store: RunStore | None`
- `capability_sources: dict[str, CapabilitySource]`
- `get_qualified_spec(qualified_name: str) -> NodeSpec`
- `record_event(event: McpEvent) -> None`
- `run_workflow_from_plan(plan, input, ...) -> RunState`
- `resume_workflow_from_plan(plan, run, ...) -> RunState`
- `workflow_artifact_catalog_entry(artifact) -> WorkflowArtifactCatalogEntry`
3. The live-source-check (`_live_source_diagnostics`) is the only MCP-specific operation. It can stay in `wf_mcp` as an extension or be gated behind a protocol-neutral `liveness_check` callback.
### First Slice Steps
1. Define `WorkflowApiBackend` protocol in `wf_api/backend.py` with the ~9 members above.
2. Create `WorkflowApi` in `wf_api/service.py` that takes `WorkflowApiBackend` instead of `WfMcpService`.
3. Move `WorkflowSurfaceHandlers` logic into `WorkflowApi` (rename or keep as alias).
4. Create `WfMcpServiceBackendAdapter` in `wf_mcp` that adapts `WfMcpService``WorkflowApiBackend`.
5. Update `wf_mcp.workflow_surface.tools` to create `WorkflowApi(WfMcpServiceBackendAdapter(service))`.
6. Update `wf_cli.context` to create `WorkflowApi(WfMcpServiceBackendAdapter(service))`.
7. Move protocol-neutral helpers (`matches_query`, `paged_list_payload`, `make_event`, etc.) to `wf_api` or `wf_platform`.
8. Keep `register_workflow_tools` in `wf_mcp` (it's the MCP tool registration layer).
## Proposed Package Shape
### First Slice (extraction)
```
src/wf_api/
__init__.py # public API: WorkflowApi, WorkflowApiBackend
backend.py # WorkflowApiBackend protocol
service.py # WorkflowApi (renamed WorkflowSurfaceHandlers)
models.py # TraceRange, NextActions, RunPayload, etc.
refs.py # parse_workflow_surface_capability_id
constants.py # DEFAULT_CALL_STEP_ID, outcomes, etc.
next_actions.py # NextActions model
saved_subgraphs.py # SavedSubgraphTree, resolve/validate
run_lifecycle.py # create_pinned_environment, persist_stopped_run, etc.
runtime_dependencies.py # resolve_runtime_dependencies
wrapper_hints.py # wrapper_hints_for_capability
```
### Later Slices
```
src/wf_api/
shared/
listing.py # matches_query, paged_list_payload (or move to wf_platform)
events.py # EventBus, make_event (rename from McpEvent)
```
### What Stays in wf_mcp
```
src/wf_mcp/
workflow_surface/
tools.py # register_workflow_tools (MCP tool registration)
__init__.py # re-exports for backward compat
broker/
service/
core.py # WfMcpService (unchanged)
adapters.py # WfMcpServiceBackendAdapter (NEW)
connections.py # ConnectionRegistry
models.py # ConnectionConfig, AuthRecord, BrokerConfig, CatalogSnapshot
storage/ # Store, FileStore
sdk/ # BackendAdapter
events/ # EventBus, McpEvent (wf_mcp-specific event bus)
shared/ # names, errors, pagination (MCP-specific utilities)
proxy/ # MCP proxy mounts
server/ # MCP server construction
runtime/ # ToolExecutor
admin_surface/ # admin tools
```
### What Stays in wf_cli
```
src/wf_cli/
context.py # CliContext now uses WorkflowApi instead of WorkflowSurfaceHandlers
commands/ # unchanged — they call context.handlers.X()
```
## Test Coverage
### Tests That Protect the Extraction
| Test File | Lines | Coverage Area |
|-----------|-------|---------------|
| `tests/wf_mcp/workflow_surface/test_artifacts.py` | 71 | list_artifacts, inspect_artifact, paging, filtering |
| `tests/wf_mcp/workflow_surface/test_capabilities.py` | 206 | list_capabilities, inspect_capability, call_capability, wrapper capabilities |
| `tests/wf_mcp/workflow_surface/test_drafts.py` | 723 | validate_draft, compile_draft, create_artifact_from_draft, draft workspaces (CRUD, patch, name, route, input/output maps), create_minimal_draft_workspace, create_draft_workspace_from_capability, create_artifact_from_workspace, create_wrapper_from_workspace |
| `tests/wf_mcp/workflow_surface/test_deployments.py` | 282 | validate_deployment, live_check, save_deployment, delete_deployment |
| `tests/wf_mcp/workflow_surface/test_runs.py` | 423 | run_deployment, resume_run, inspect_run, read_run_trace, interrupts, reducers, saved subgraphs |
| `tests/wf_mcp/workflow_surface/test_wrappers.py` | 172 | create_artifact_from_plan (wrapper), call_capability (wrapper), logical refs |
| `tests/wf_mcp/workflow_surface/test_next_actions.py` | 148 | NextActions model behavior |
| `tests/wf_mcp/workflow_surface/conftest.py` | 380 | Shared fixtures: handlers(), artifact(), echo_artifact(), echo_draft(), etc. |
| `tests/wf_cli/test_context.py` | 33 | load_cli_context builds service and handlers |
| `tests/wf_cli/test_app.py` | 88 | CLI help output (smoke tests) |
| `tests/wf_cli/test_run_deploy.py` | 218 | CLI deploy validate, run start, run inspect, run trace |
| `tests/wf_cli/test_discovery_lifecycle.py` | 370 | CLI cap list, artifact list, draft create-from-capability, deploy save, render formats |
| `tests/wf_cli/test_explain.py` | 174 | explain command (diagnostic code lookup) |
| `tests/wf_mcp/test_workflow_surface_refs.py` | — | parse_workflow_surface_capability_id |
| `tests/wf_mcp/test_workflow_wrapper_hints.py` | — | wrapper_hints_for_capability |
| `tests/wf_mcp/test_saved_subgraphs.py` | — | SavedSubgraphTree resolution and validation |
### Missing Tests Needed Before Extraction
- **Integration test for `WorkflowApi` with a mock backend**: Verify that `WorkflowApi` works correctly when given a `WorkflowApiBackend` that is NOT `WfMcpService`. This catches accidental coupling.
- **Test that `wf_cli` works with the new `WorkflowApi` path**: The existing `test_run_deploy.py` uses `patch("wf_cli.commands.runs.load_cli_context", ...)` — need to verify the patching surface doesn't change unexpectedly.
- **Negative test for live-source-check separation**: Verify that `validate_deployment(live_check=True)` still works after the MCP-specific liveness probe is decoupled.
## Things To Keep In wf_mcp For Now
- MCP transport/proxy/runtime/session code (`src/wf_mcp/proxy/`, `src/wf_mcp/server/`, `src/wf_mcp/runtime/`)
- FastMCP registration tools (`src/wf_mcp/workflow_surface/tools.py``register_workflow_tools`)
- Connection adapters (`src/wf_mcp/sdk/`, `src/wf_mcp/broker/service/adapters.py`)
- Connection registry (`src/wf_mcp/connections.py`)
- Broker server construction (`src/wf_mcp/broker/server.py`, `src/wf_mcp/broker/config.py`)
- MCP-specific auth (`src/wf_mcp/models.py:AuthRecord`)
- MCP catalog storage (`src/wf_mcp/storage/`)
- Admin surface (`src/wf_mcp/admin_surface/`)
- MCP event bus (`src/wf_mcp/events/`) — may need a protocol-neutral fork
- Discovery/refresh (`src/wf_mcp/broker/discovery.py`)
## Risks And Open Questions
### Circular Import Risks
- **Current**: `wf_mcp.workflow_surface.handlers` imports from `wf_mcp.broker.service` (type-checking only via `TYPE_CHECKING`). No circular import today.
- **After extraction**: `wf_api.service` must NOT import from `wf_mcp`. The `WfMcpServiceBackendAdapter` lives in `wf_mcp` and imports from `wf_api`. Direction: `wf_mcp``wf_api`, never reverse.
- **Risk**: The `live_source_diagnostics` helper currently imports `require_adapter` from `wf_mcp.broker.service.adapters`. This must stay in `wf_mcp` or be passed as a callback.
### Private Method Dependencies
- `self.service._get_qualified_spec(qualified_name)` — used 3 times. This is a private method on `WfMcpService`. Must become a port method.
- `self.service._record_event(event)` — used 7 times. This is a private method. Must become a port method (or the event bus itself becomes a port).
### Store Ownership Ambiguity
- `WfMcpService.__post_init__` creates default `FileWorkflowArtifactStore`, `FileDraftWorkspaceStore`, `FileRunStore` if not provided. These are protocol-neutral stores.
- The stores are created from `_store_root(self.store)` which uses the MCP `Store` root.
- After extraction, store creation should be the caller's responsibility (config-driven), not `WfMcpService`'s.
### Naming Confusion
- `McpEvent` / `EventBus` — the name says "Mcp" but the event system is protocol-neutral. Rename to `DomainEvent` / `EventBus` or move to `wf_platform`.
- `WorkflowSurfaceHandlers` — the name suggests MCP surface. Rename to `WorkflowApi`.
- `wf_mcp.workflow_surface` — the package name implies MCP. The extracted package becomes `wf_api`.
### Tests That Monkeypatch `wf_cli.commands.*.load_cli_context`
- `tests/wf_cli/test_run_deploy.py` patches `wf_cli.commands.deployments.load_cli_context`, `wf_cli.commands.runs.load_cli_context`
- `tests/wf_cli/test_discovery_lifecycle.py` patches `wf_cli.commands.caps.load_cli_context`, `tests/wf_cli/test_discovery_lifecycle.py` patches multiple command modules
- These patches inject `_load_cli_context_with_specs` which calls `load_cli_context` then `register_specs`. After extraction, `load_cli_context` will construct `WorkflowApi` instead of `WorkflowSurfaceHandlers`. The patches should still work as long as `CliContext.handlers` remains the same attribute name.
### FastAPI Implications
- The `WorkflowApiBackend` port pattern makes FastAPI trivial: create a `FastApiBackendAdapter` that provides the same stores/sources/execution without MCP.
- The MCP-specific live-source-check can be an opt-in dependency injected into the API.
- `RawWorkflowPlan` should move to `wf_api.models` or `wf_core` so FastAPI can use it without importing `wf_mcp`.
### Other Weirdness
- `WorkflowSurfaceHandlers._wrapper_artifact_for_capability_name` uses `parse_workflow_surface_capability_id` from `wf_mcp.workflow_surface.refs`. This parser understands the `workflow.<id>.v<version>` naming convention. It's protocol-neutral and should move to `wf_api.refs`.
- `_available_sources` (line 1238) is a module-level function that takes `WfMcpService` directly. It should take the port interface instead, or become a method on `WorkflowApi`.
- `_live_source_diagnostics` (line 1277) is the only function that needs MCP adapters/connections. It should be injected as an optional callback or stay as a `wf_mcp` extension.
## Suggested Next Plan
1. **Define the port interface** (`WorkflowApiBackend`) in a new `src/wf_api/backend.py`. Start with the 9 members identified above.
2. **Create `WorkflowApi`** in `src/wf_api/service.py` by copying `WorkflowSurfaceHandlers` and changing `self.service: WfMcpService` to `self.backend: WorkflowApiBackend`.
3. **Create adapter** in `src/wf_mcp/broker/service/adapters.py` (or a new file) that wraps `WfMcpService` into `WorkflowApiBackend`.
4. **Update consumers** (`wf_mcp.workflow_surface.tools`, `wf_cli.context`) to use `WorkflowApi(WfMcpServiceBackendAdapter(service))`.
5. **Move protocol-neutral modules** (`next_actions`, `saved_subgraphs`, `run_lifecycle`, `runtime_dependencies`, `wrapper_hints`, `refs`, `constants`, `models`) to `wf_api`.
6. **Move listing utilities** (`matches_query`, `paged_list_payload`) to `wf_platform` or `wf_api.shared`.
7. **Rename event types** from `McpEvent` to `DomainEvent` (or create a protocol-neutral fork).
8. **Run full test suite** — all existing tests must pass with import-only changes.
9. **Update `CliContext`** to use `WorkflowApi` instead of `WorkflowSurfaceHandlers`.
10. **Add integration test** for `WorkflowApi` with a mock backend (not `WfMcpService`).
### Dependency Graph After Extraction
```
wf_api (new)
├── depends on: wf_artifacts, wf_platform, wf_authoring, wf_core
└── defines: WorkflowApiBackend (protocol), WorkflowApi (implementation)
wf_mcp
├── depends on: wf_api, wf_artifacts, wf_platform, wf_authoring, wf_core
├── adapts: WfMcpService → WorkflowApiBackend
├── registers: FastMCP tools via register_workflow_tools (uses WorkflowApi)
└── owns: connections, adapters, proxy, server, admin, MCP events, MCP storage
wf_cli
├── depends on: wf_api, wf_mcp (for service construction)
├── uses: WorkflowApi via CliContext
└── owns: CLI commands, formatting, I/O
```
@@ -0,0 +1,229 @@
# wf_api Extraction Mapping Prompt
You are investigating the codebase only. Do not edit files.
Goal: map how the current workflow application surface is implemented so we can extract a protocol-neutral `wf_api` package without breaking MCP, CLI, stores, or tests.
The current suspicion:
- `src/wf_mcp/workflow_surface/handlers.py` is a large application-service class.
- It depends on `WfMcpService`, which is also broad.
- We need to understand whether to move the class first, split it first, or introduce smaller services/facades.
## Required Output
Write your findings to:
```text
random shit/wf_api_extraction_map.md
```
Keep it structured and link-heavy. Do not paste huge code blocks. Use file paths, symbol names, and short notes.
## Investigation Scope
Focus on these files/symbols first:
```text
src/wf_mcp/workflow_surface/handlers.py
WorkflowSurfaceHandlers
src/wf_mcp/broker/service/core.py
WfMcpService
src/wf_mcp/workflow_surface/tools.py
register_workflow_tools
src/wf_cli/context.py
CliContext
load_cli_context
src/wf_cli/commands/*.py
src/wf_artifacts/
src/wf_platform/
src/wf_core/
```
Use ripgrep/symbol search. Prefer precise symbol references over broad file dumps.
## Questions To Answer
### 1. What are the public operations?
List every `WorkflowSurfaceHandlers` public method and classify it:
```text
capabilities
drafts
artifacts
deployments
runs
explain/next-actions
internal helper accidentally public
```
For each method, record:
- method name
- input parameters
- return shape summary
- direct dependencies
- current callers/tests
### 2. What parts of `WfMcpService` does it actually use?
For each access from `WorkflowSurfaceHandlers` to `self.service`, record the member:
```text
self.service.artifact_store
self.service.draft_workspace_store
self.service.capability_sources
self.service._get_qualified_spec(...)
self.service._record_event(...)
...
```
Classify each dependency:
```text
artifact storage
draft storage
run storage
source/capability inventory
MCP connection/catalog
event bus
private helper currently being used
```
Flag private-method dependencies such as `_get_qualified_spec` or `_record_event`.
### 3. Which dependencies are MCP-specific vs protocol-neutral?
Create a table:
```text
Dependency | Package today | MCP-specific? | Should live in wf_artifacts/wf_platform/wf_api/wf_mcp?
```
Examples:
- artifact store likely protocol-neutral
- draft workspace store likely protocol-neutral
- source/capability refs likely protocol-neutral
- MCP connection config/adapters likely MCP-specific for now
- event bus may be platform-neutral
### 4. What is the safest extraction seam?
Compare these options:
#### Option A: Move class first
Move `WorkflowSurfaceHandlers` to `wf_api.service.WorkflowApi`, keep same constructor accepting `WfMcpService`.
Pros/cons.
#### Option B: Introduce API facade with ports
Create `WorkflowApi` that depends on a smaller `WorkflowApiBackend`/ports object instead of all `WfMcpService`, then adapt `WfMcpService` into that backend.
Pros/cons.
#### Option C: Split handlers by domain first
Split capabilities/drafts/artifacts/deployments/runs into separate classes before moving packages.
Pros/cons.
Recommend one first slice and explain why.
### 5. What should the target package shape be?
Propose a concrete package layout, for example:
```text
src/wf_api/
__init__.py
service.py
backend.py
capabilities.py
drafts.py
artifacts.py
deployments.py
runs.py
models.py
```
Do not overdesign. Identify which files are needed in the first slice vs later.
### 6. What tests protect the extraction?
List existing tests that must continue to pass, grouped by package:
```text
tests/wf_mcp/workflow_surface/*
tests/wf_cli/*
tests/wf_mcp/test_server.py
...
```
Identify any missing tests needed before extraction.
### 7. What code should not move yet?
Explicitly list things that should stay in `wf_mcp` for now:
- MCP transport/proxy/runtime/session code
- FastMCP registration tools
- connection adapters if not protocol-neutral yet
- broker server construction
### 8. Risks and weirdness
Call out:
- circular import risks
- private method dependencies
- store ownership ambiguity
- naming confusion
- tests that monkeypatch `wf_cli.commands.*.load_cli_context`
- anything that would make FastAPI later harder or easier
## Output Format
Use this exact outline:
```markdown
# wf_api Extraction Map
## Executive Summary
## Public Operation Inventory
## WfMcpService Dependency Inventory
## Protocol-Neutral vs MCP-Specific Dependencies
## Extraction Options
## Recommended First Slice
## Proposed Package Shape
## Test Coverage
## Things To Keep In wf_mcp For Now
## Risks And Open Questions
## Suggested Next Plan
```
## Constraints
- Do not edit code.
- Do not propose FastAPI implementation yet.
- Keep current process-local behavior as the default.
- Assume `wf_mcp` and `wf_cli` should become adapters over the same process-local API.
- Prefer small, behavior-preserving extraction steps over a large rewrite.
+15
View File
@@ -0,0 +1,15 @@
{
"$schema": "https://opencode.ai/config.json",
"lsp": {
"python": {
"command": ["uv", "run", "pyright", "--stdio"],
"extensions": [".py"]
}
},
"mcp": {
"serena": {
"type": "local",
"command": ["serena", "start-mcp-server", "--project-from-cwd"],
}
}
}
+1 -5
View File
@@ -1,5 +1 @@
*
!diagram.patch
!pandoc-diagram.ps1
!diagram.lua
!.gitignore
diagram-og.lua