Files
lda-wf/docs/schema_validation.md
T
lda Verified 95ec73d518 even MORE code review
stateful to not deadhang, graph context -> output, more explicit state errors, other misc changes idfk any of those good thing theyre fixed tho
2026-05-26 02:33:41 +07:00

120 lines
4.3 KiB
Markdown

# Schema Validation Boundary
`wf_core` uses `SchemaRef` to carry JSON-schema-like shapes on workflow input,
node input, node output, and workflow output. Runtime payload validation is
delegated to the existing `jsonschema` library.
## Current Behavior
`wf_core.runtime.ops.schemas.validate_payload_against_schema` currently:
- converts `SchemaRef` into a JSON Schema dictionary
- asks `jsonschema` to validate the schema itself
- asks `jsonschema` to validate the payload
- wraps validation failures in `WorkflowExecutionError`
This means normal JSON Schema checks such as object shape, required fields,
property types, nested required fields, arrays, and item types are enforced by
the library.
It still does not solve:
- semantic compatibility between Pydantic-generated schemas and every possible
external JSON Schema dialect
- typed Python object creation from arbitrary JSON Schema
- workflow state merge behavior beyond declared exact-path metadata
- deep node-local map-path validation beyond statically knowable schema roots
- better domain-specific error payloads beyond `WorkflowExecutionError`
This means schema fields are contracts for authoring, planning, documentation,
and mapping validation. Runtime state merge behavior is separate metadata on
declared exact state paths; undeclared paths still use `replace`.
Final workflow output has two projection modes:
- If `Workflow.output` contains bindings, those bindings build the public output
payload from graph paths such as `state.result.message`.
- If `Workflow.output` is empty, legacy projection copies same-named top-level
state keys listed in `workflow.output_schema.properties`.
Prefer explicit `Workflow.output` bindings for new workflows. The same-named
state projection exists for compatibility with older plans.
The projected payload is then validated against `workflow.output_schema`.
## Why This Matters
Node and workflow boundaries can now reject wrong primitive/container types when
the schema declares them. This matters before workflows are generated by an LLM
or backed by arbitrary MCP tools.
## Authoring Footguns
### Prefer explicit entry arrays at LLM-facing boundaries
`dict[str, SomeModel]` is often pleasant in Python but weak as a human- or
LLM-facing schema. It commonly appears as a generic object with arbitrary keys,
and many clients communicate or render it much less clearly than an explicit
list shape.
Prefer:
```python
class Entry(BaseModel):
key: str
value: SomeModel
entries: list[Entry]
```
over:
```python
entries: dict[str, SomeModel]
```
when the schema is meant for MCP tools, LLM planning, or durable workflow
authoring contracts. Internal Python state can still use dictionaries when that
is the right runtime shape.
### Do not assume all JSON Schema consumers handle references equally
Pydantic-generated schemas may use `$defs` and local `$ref` references for
nested models. The runtime delegates validation to `jsonschema`, which supports
that structure, but display layers and downstream consumers may vary in how well
they present or reason about referenced shapes.
`wf_authoring` resolves the local Pydantic `$ref -> $defs` pattern only for its
own state-field projection. That helper is not a general-purpose JSON Schema
flattener, and arbitrary external schemas should not be assumed to share the
same shape.
## Intended Seam
The schema adapter lives behind:
```text
wf_core.runtime.ops.schemas.validate_payload_against_schema
```
Callers should not choose or invoke the validation backend directly. The runtime
keeps one small validation interface and hides the validation backend.
## 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.
- 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.
- Add targeted tests as externally sourced schemas grow more complex.
## 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.
- Do not hand-write a general JSON Schema implementation.