wf reorg 5

This commit is contained in:
lda
2026-05-08 22:02:40 +07:00 Verified
parent 703b25bc19
commit 8cb4e213d9
21 changed files with 170 additions and 28 deletions
+62
View File
@@ -0,0 +1,62 @@
# Project Map
This repository has three main packages plus examples and tests.
## Packages
| Package | Purpose | Usual callers |
| --- | --- | --- |
| `wf_core` | Deterministic workflow kernel: models, validation, runtime, run state, traces, interrupts, foreach, and path/state operations. | Runtime users, `wf_authoring`, workflow adapters. |
| `wf_authoring` | Ergonomic workflow construction: `@node`, `NodeSpec`, builder DSL, conditions, path helpers, reusable ops, subgraph nodes. | Humans, tests, future LLM workflow builders. |
| `wf_mcp` | MCP integration: SDK adapters, broker/proxy runtime, storage, config control, transparent proxy, and workflow wrappers for discovered tools. | MCP-facing CLI/server code and future UI/control surfaces. |
## Important Entry Points
- `wf_core`: public kernel facade for common runtime/model imports.
- `wf_core.runtime`: `execute_workflow`, `resume_workflow`, `step_workflow`,
and async variants.
- `wf_core.models`: concrete Pydantic workflow model package.
- `wf_core.validation`: structural workflow validation.
- `wf_authoring`: public authoring facade.
- `wf_authoring.WorkflowBuilder`: graph construction.
- `wf_authoring.node`: typed Python function to `NodeSpec`.
- `wf_mcp`: public MCP facade.
- `wf-mcp`: CLI script from `pyproject.toml`.
## Examples
`examples/demo_workflow.py` contains the declared demo workflow and demo node
registry used by `main.py` and workflow tests. It is intentionally outside
`wf_core` so the kernel package does not carry fixture/demo code.
## Tests
- `tests/authoring`: builder, node decorator, ops, async runtime, subgraph, and
demo workflow comparisons.
- `tests/wf_mcp`: MCP SDK adapter, broker, transparent proxy, storage, CLI, and
naming behavior.
- `tests/rewrite`: local rewrite/port experiments that should keep exercising
real user ergonomics.
- `tests/fixtures`: test-only helper servers and fixtures.
## Verification Commands
```powershell
uv run --with pytest pytest -q
uv run ruff check src tests main.py examples
uv run basedpyright src\wf_core tests\authoring tests\rewrite examples main.py --level error
```
Use `uv run --env-file .env --with pytest pytest -q` when live MCP-backed tests
need local environment configuration.
## Where To Add Things
- Add new executable workflow semantics in `wf_core.runtime` / `wf_core.runtime.ops`.
- Add new graph/model syntax in `wf_core.models`, then validate it in
`wf_core.validation`.
- Add author convenience helpers in `wf_authoring`, not `wf_core`.
- Add MCP transport/proxy/config behavior in `wf_mcp` concern packages.
- Add runnable examples in `examples`.
- Add test-only servers or helpers in `tests/fixtures`.
+64
View File
@@ -0,0 +1,64 @@
# Schema Validation Boundary
`wf_core` uses `SchemaRef` to carry JSON-schema-like shapes on workflow input,
node input, node output, and workflow output. The current runtime does not
implement full JSON Schema validation.
## Current Behavior
`wf_core.runtime.ops.schemas.validate_payload_against_schema` currently checks:
- if `schema.type == "object"`, the payload must be a `dict`
- required top-level keys must be present
It does not currently check:
- property value types, such as `string`, `boolean`, `array`, or nested objects
- array item schemas
- `additionalProperties`
- formats, enums, minimums, maximums, unions, discriminators, or nested required
fields
- whether a schema is valid JSON Schema
This means schema fields are mostly contracts for authoring, planning,
documentation, and mapping validation today. They are not yet strong runtime
guards.
## Why This Matters
The engine currently looks stricter than it is. A node can return a required
field with the wrong type and pass runtime validation as long as the field is
present. That is acceptable during early design work, but it is a real product
boundary before workflows are generated by an LLM or backed by arbitrary MCP
tools.
## Intended Seam
The schema adapter should live behind:
```text
wf_core.runtime.ops.schemas.validate_payload_against_schema
```
Callers should not choose or invoke the validation backend directly. The runtime
should keep one small validation interface and hide whether the implementation
uses Pydantic, `jsonschema`, a generated model cache, or a stricter custom
adapter.
## Future Requirements
- Validate workflow input, node input, node output, and final workflow output
with the same semantics.
- Return errors that name the failing boundary and path.
- Avoid silently accepting unsupported schema features once schemas are
user/LLM-authored.
- Keep schema validation separate from graph structure validation.
- Keep `wf_authoring` free to generate schemas from Pydantic models without
making the core runtime depend on authoring internals.
## Non-Goals For Now
- Do not add ad hoc type checks throughout runtime state operations.
- Do not let each node wrapper invent separate validation behavior.
- Do not conflate graph validation with payload validation.
+9
View File
@@ -62,6 +62,13 @@ raising at the first failure.
- `wf_core.__init__` should stay a curated public facade, not a dump of runtime - `wf_core.__init__` should stay a curated public facade, not a dump of runtime
internals. internals.
## Schema Validation
Payload schema validation is intentionally isolated behind
`wf_core.runtime.ops.schemas.validate_payload_against_schema`. That function is
not a full JSON Schema engine today; see `docs/schema_validation.md` for the
current limits and intended adapter seam.
## What This Cleanup Does Not Solve Yet ## What This Cleanup Does Not Solve Yet
- Foreach is still serial-only. Parallel foreach needs an explicit scheduling - Foreach is still serial-only. Parallel foreach needs an explicit scheduling
@@ -72,3 +79,5 @@ raising at the first failure.
- Runtime errors are still ordinary exceptions plus failed run status. A richer - Runtime errors are still ordinary exceptions plus failed run status. A richer
error payload can be added later, but should be designed as part of trace/run error payload can be added later, but should be designed as part of trace/run
state rather than scattered exceptions. state rather than scattered exceptions.
- Payload schema validation is still shallow. The runtime checks object payloads
and required top-level keys, not full JSON Schema semantics.
+2
View File
@@ -3,7 +3,9 @@
## Related docs ## Related docs
- [docs/authoring_sketch.md](docs/authoring_sketch.md): `wf_authoring` direction, including `@node`, `NodeSpec`, builder ergonomics, async registry seams, and subgraph-as-node. - [docs/authoring_sketch.md](docs/authoring_sketch.md): `wf_authoring` direction, including `@node`, `NodeSpec`, builder ergonomics, async registry seams, and subgraph-as-node.
- [docs/project_map.md](docs/project_map.md): current package map, entrypoints, examples, tests, and verification commands.
- [docs/wf_core_architecture.md](docs/wf_core_architecture.md): current `wf_core` package boundaries, runtime flow, validation flow, and remaining cleanup seams. - [docs/wf_core_architecture.md](docs/wf_core_architecture.md): current `wf_core` package boundaries, runtime flow, validation flow, and remaining cleanup seams.
- [docs/schema_validation.md](docs/schema_validation.md): current payload schema validation limits and intended validation seam.
- [docs/wf_mcp_plan.md](docs/wf_mcp_plan.md): `wf_mcp` direction as a namespaced MCP capability broker plus workflow build/run layer. - [docs/wf_mcp_plan.md](docs/wf_mcp_plan.md): `wf_mcp` direction as a namespaced MCP capability broker plus workflow build/run layer.
- [docs/wf_mcp_architecture.md](docs/wf_mcp_architecture.md): current `wf_mcp` package boundaries and dependency rules. - [docs/wf_mcp_architecture.md](docs/wf_mcp_architecture.md): current `wf_mcp` package boundaries and dependency rules.
- [docs/scratchpad.md](docs/scratchpad.md): rougher design history and intermediate spec notes that fed the current model. - [docs/scratchpad.md](docs/scratchpad.md): rougher design history and intermediate spec notes that fed the current model.
+1 -1
View File
@@ -18,7 +18,7 @@ from wf_core import (
execute_workflow, execute_workflow,
) )
from wf_core.errors import WorkflowExecutionError from wf_core.errors import WorkflowExecutionError
from wf_core.models import Condition as CoreCondition from wf_core.models.conditions import Condition as CoreCondition
from ..dsl import Expr, PathArg, compile_condition from ..dsl import Expr, PathArg, compile_condition
from ..nodes.callables import SyncRegistryHandler from ..nodes.callables import SyncRegistryHandler
+1 -1
View File
@@ -3,7 +3,7 @@ from __future__ import annotations
from dataclasses import dataclass from dataclasses import dataclass
from typing import Literal from typing import Literal
from wf_core.models import ( from wf_core.models.conditions import (
BinaryCondition, BinaryCondition,
Condition, Condition,
ExistsCondition, ExistsCondition,
+2 -2
View File
@@ -4,15 +4,15 @@ from collections.abc import Mapping
from typing import Any from typing import Any
from .errors import WorkflowExecutionError from .errors import WorkflowExecutionError
from .models import ( from .models.conditions import (
BinaryCondition, BinaryCondition,
Condition, Condition,
ExistsCondition, ExistsCondition,
LiteralOperand, LiteralOperand,
NotCondition, NotCondition,
PathOperand, PathOperand,
VariadicCondition,
) )
from .models.conditions import VariadicCondition
from .paths import PathResolutionError, path_exists, resolve_graph_path from .paths import PathResolutionError, path_exists, resolve_graph_path
+1 -1
View File
@@ -3,7 +3,7 @@ from __future__ import annotations
from collections.abc import Mapping from collections.abc import Mapping
from typing import Any from typing import Any
from wf_core.models import Workflow from wf_core.models.workflow import Workflow
from wf_core.runtime.ops.flow import finalize_run from wf_core.runtime.ops.flow import finalize_run
from wf_core.runtime.ops.frames import collapse_completed_frames from wf_core.runtime.ops.frames import collapse_completed_frames
from wf_core.runtime.ops.nodes import AsyncNodeHandler, NodeHandler from wf_core.runtime.ops.nodes import AsyncNodeHandler, NodeHandler
+1 -1
View File
@@ -2,7 +2,7 @@ from __future__ import annotations
from typing import Any from typing import Any
from wf_core.models import Workflow from wf_core.models.workflow import Workflow
from wf_core.run_state import ( from wf_core.run_state import (
ExecutionFrame, ExecutionFrame,
FrameStatus, FrameStatus,
+2 -1
View File
@@ -2,7 +2,8 @@ from __future__ import annotations
from wf_core.conditions import safe_resolve_path from wf_core.conditions import safe_resolve_path
from wf_core.errors import WorkflowExecutionError from wf_core.errors import WorkflowExecutionError
from wf_core.models import ForeachNode, Workflow from wf_core.models.steps import ForeachNode
from wf_core.models.workflow import Workflow
from wf_core.run_state import ExecutionFrame, FrameStatus, RunState, StepExecutionResult from wf_core.run_state import ExecutionFrame, FrameStatus, RunState, StepExecutionResult
from wf_core.runtime.ops.flow import advance_frame, append_step_result_trace from wf_core.runtime.ops.flow import advance_frame, append_step_result_trace
from wf_core.runtime.ops.frames import frame_context_values from wf_core.runtime.ops.frames import frame_context_values
+1 -1
View File
@@ -1,7 +1,7 @@
from __future__ import annotations from __future__ import annotations
from wf_core.conditions import eval_condition from wf_core.conditions import eval_condition
from wf_core.models import ConditionNode, InterruptNode from wf_core.models.steps import ConditionNode, InterruptNode
from wf_core.run_state import FrameStatus, RunState, RunStatus, StepExecutionResult from wf_core.run_state import FrameStatus, RunState, RunStatus, StepExecutionResult
from wf_core.runtime.ops.flow import append_trace from wf_core.runtime.ops.flow import append_trace
from wf_core.runtime.ops.frames import frame_context_values from wf_core.runtime.ops.frames import frame_context_values
+2 -1
View File
@@ -4,7 +4,8 @@ from dataclasses import dataclass
from typing import Any from typing import Any
from wf_core.errors import WorkflowExecutionError from wf_core.errors import WorkflowExecutionError
from wf_core.models import NodeDef, Workflow from wf_core.models.schemas import NodeDef
from wf_core.models.workflow import Workflow
@dataclass(slots=True) @dataclass(slots=True)
+2 -1
View File
@@ -4,7 +4,8 @@ from typing import Any
from wf_core.conditions import safe_resolve_path from wf_core.conditions import safe_resolve_path
from wf_core.errors import WorkflowExecutionError from wf_core.errors import WorkflowExecutionError
from wf_core.models import InterruptNode, Workflow from wf_core.models.steps import InterruptNode
from wf_core.models.workflow import Workflow
from wf_core.run_state import InterruptRequest, RunState, StepExecutionResult from wf_core.run_state import InterruptRequest, RunState, StepExecutionResult
from wf_core.runtime.ops.flow import advance_frame, append_step_result_trace from wf_core.runtime.ops.flow import advance_frame, append_step_result_trace
from wf_core.runtime.ops.index import WorkflowIndex from wf_core.runtime.ops.index import WorkflowIndex
+4 -1
View File
@@ -5,7 +5,10 @@ from typing import Any, cast
from wf_core.conditions import safe_resolve_path from wf_core.conditions import safe_resolve_path
from wf_core.errors import WorkflowExecutionError from wf_core.errors import WorkflowExecutionError
from wf_core.models import NodeDef, NodeResult, NodeUse, Workflow from wf_core.models.results import NodeResult
from wf_core.models.schemas import NodeDef
from wf_core.models.steps import NodeUse
from wf_core.models.workflow import Workflow
from wf_core.run_state import RunState, RuntimeContext, StepExecutionResult from wf_core.run_state import RunState, RuntimeContext, StepExecutionResult
from wf_core.runtime.ops.frames import frame_context_values from wf_core.runtime.ops.frames import frame_context_values
from wf_core.runtime.ops.schemas import validate_payload_against_schema from wf_core.runtime.ops.schemas import validate_payload_against_schema
+1 -1
View File
@@ -2,7 +2,7 @@ from __future__ import annotations
from copy import deepcopy from copy import deepcopy
from wf_core.models import Workflow from wf_core.models.workflow import Workflow
from wf_core.run_state import ExecutionFrame, FrameStatus, RunState, RunStatus from wf_core.run_state import ExecutionFrame, FrameStatus, RunState, RunStatus
+2 -1
View File
@@ -3,7 +3,8 @@ from __future__ import annotations
from typing import Any from typing import Any
from wf_core.errors import WorkflowExecutionError from wf_core.errors import WorkflowExecutionError
from wf_core.models import NodeUse, Workflow from wf_core.models.steps import NodeUse
from wf_core.models.workflow import Workflow
from wf_core.paths import ( from wf_core.paths import (
PathResolutionError, PathResolutionError,
get_nested_value, get_nested_value,
+1 -1
View File
@@ -3,7 +3,7 @@ from __future__ import annotations
from typing import Any from typing import Any
from wf_core.errors import WorkflowExecutionError from wf_core.errors import WorkflowExecutionError
from wf_core.models import Workflow from wf_core.models.workflow import Workflow
from wf_core.runtime.ops.frames import collapse_completed_frames from wf_core.runtime.ops.frames import collapse_completed_frames
from wf_core.runtime.ops.index import WorkflowIndex, build_workflow_index from wf_core.runtime.ops.index import WorkflowIndex, build_workflow_index
from wf_core.runtime.ops.interrupts import resume_interrupt from wf_core.runtime.ops.interrupts import resume_interrupt
+2 -2
View File
@@ -4,14 +4,14 @@ from collections.abc import Mapping
from typing import Any from typing import Any
from wf_core.errors import WorkflowExecutionError from wf_core.errors import WorkflowExecutionError
from wf_core.models import ( from wf_core.models.steps import (
ConditionNode, ConditionNode,
ForeachNode, ForeachNode,
InterruptNode, InterruptNode,
JoinNode, JoinNode,
NodeUse, NodeUse,
Workflow,
) )
from wf_core.models.workflow import Workflow
from wf_core.runtime.ops.flow import advance_frame, append_step_result_trace from wf_core.runtime.ops.flow import advance_frame, append_step_result_trace
from wf_core.runtime.ops.foreach import step_foreach from wf_core.runtime.ops.foreach import step_foreach
from wf_core.runtime.ops.handlers import ( from wf_core.runtime.ops.handlers import (
+3 -4
View File
@@ -1,15 +1,14 @@
from __future__ import annotations from __future__ import annotations
from wf_core.models import ( from wf_core.models.steps import (
ConditionNode, ConditionNode,
Edge,
ForeachNode, ForeachNode,
InterruptNode, InterruptNode,
NodeDef,
NodeUse, NodeUse,
Step, Step,
Workflow,
) )
from wf_core.models.schemas import NodeDef
from wf_core.models.workflow import Edge, Workflow
from wf_core.tokens import END from wf_core.tokens import END
from wf_core.validation.issues import ValidationIssueCode, ValidationReport from wf_core.validation.issues import ValidationIssueCode, ValidationReport
from wf_core.validation.outcomes import declared_outcomes_for_step, reachable_node_ids from wf_core.validation.outcomes import declared_outcomes_for_step, reachable_node_ids
+3 -1
View File
@@ -1,6 +1,8 @@
from __future__ import annotations from __future__ import annotations
from wf_core.models import Edge, InterruptNode, NodeDef, NodeUse, Step from wf_core.models.schemas import NodeDef
from wf_core.models.steps import InterruptNode, NodeUse, Step
from wf_core.models.workflow import Edge
from wf_core.tokens import END from wf_core.tokens import END
+4 -7
View File
@@ -1,20 +1,17 @@
from __future__ import annotations from __future__ import annotations
from wf_core.models import ( from wf_core.models.conditions import (
BinaryCondition, BinaryCondition,
Condition, Condition,
ConditionNode,
ExistsCondition, ExistsCondition,
ForeachNode,
InterruptNode,
LiteralOperand, LiteralOperand,
NodeDef,
NodeUse,
NotCondition, NotCondition,
PathOperand, PathOperand,
VariadicCondition, VariadicCondition,
Workflow,
) )
from wf_core.models.schemas import NodeDef
from wf_core.models.steps import ConditionNode, ForeachNode, InterruptNode, NodeUse
from wf_core.models.workflow import Workflow
from wf_core.paths import is_valid_destination_path, is_valid_source_path from wf_core.paths import is_valid_destination_path, is_valid_source_path
from wf_core.validation.issues import ValidationIssueCode, ValidationReport from wf_core.validation.issues import ValidationIssueCode, ValidationReport