8.0 KiB
JSON Schema State Reducers Implementation Plan
For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Make Workflow.state_schema a normal JSON Schema object, with reducer as an explicit workflow extension keyword on field schemas.
Architecture: StateSchema should validate as JSON Schema first, then expose helper indexes for workflow runtime metadata. Runtime reducer lookup should compile from properties paths instead of requiring a separate path declaration list. Legacy fields inputs remain parse-only compatibility during the transition.
Tech Stack: Python, Pydantic v2, jsonschema, wf_core path models, pytest, basedpyright, ruff.
Task 1: Add Canonical State Schema Tests
Files:
-
Modify:
tests/core/test_nested_state_paths.py -
Modify:
tests/core/test_schema_validation.py -
Step 1: Add a test for JSON Schema property reducers
def test_state_schema_uses_json_schema_properties_as_canonical_shape() -> None:
schema = StateSchema.model_validate(
{
"type": "object",
"properties": {
"person": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "Display name",
"reducer": "wf.std.replace",
}
},
},
"count": {"type": "integer", "reducer": "wf.std.add"},
},
}
)
fields = schema.field_map()
assert fields["person.name"].validation_schema.type == "string"
assert fields["person.name"].reducer == ReducerRef(name="wf.std.replace")
assert fields["count"].reducer == ReducerRef(name="wf.std.add")
- Step 2: Add a dump test proving the canonical output is still JSON Schema
def test_state_schema_dumps_canonical_json_schema_with_reducer_keyword() -> None:
schema = StateSchema.model_validate(
{
"type": "object",
"properties": {
"count": {"type": "integer", "reducer": "wf.std.add"}
},
}
)
dumped = schema.model_dump(mode="json")
assert dumped["type"] == "object"
assert dumped["properties"]["count"]["type"] == "integer"
assert dumped["properties"]["count"]["reducer"] == "wf.std.add"
Draft202012Validator.check_schema(dumped)
- Step 3: Add a runtime reducer lookup test from canonical schema
def test_exact_nested_state_path_uses_reducer_from_json_schema_property() -> None:
workflow = _workflow_from_state_schema(
StateSchema.model_validate(
{
"type": "object",
"properties": {
"person": {
"type": "object",
"properties": {
"tags": {"type": "array", "reducer": "wf.std.append"}
},
}
},
}
)
)
state = {"person": {"tags": ["seed"]}}
write_state_value(workflow, state, "state.person.tags", ["next"])
assert state["person"]["tags"] == ["seed", "next"]
- Step 4: Run focused tests and confirm failures
Run: uv run --with pytest pytest tests/core/test_nested_state_paths.py tests/core/test_schema_validation.py -q
Expected: new tests fail because StateSchema still serializes as fields: [...] and reducer lookup is compiled from field declarations only.
Task 2: Implement JSON-Schema-Native StateSchema
Files:
-
Modify:
src/wf_core/models/schemas.py -
Step 1: Make
StateSchemainherit JSON Schema fields directly
StateSchema should expose common JSON Schema object fields:
title: str | None = None
type: str | list[str] | None = "object"
properties: dict[str, Any] = Field(default_factory=dict)
required: list[str] = Field(default_factory=list)
- Step 2: Preserve legacy
fieldsas parse-only input
Keep accepting:
{
"fields": [
{ "path": "state.count", "type": "integer", "reducer": "wf.std.add" }
]
}
and:
{ "fields": { "count": { "type": "integer", "reducer": "wf.std.add" } } }
by converting both into:
{
"type": "object",
"properties": { "count": { "type": "integer", "reducer": "wf.std.add" } }
}
- Step 3: Add
field_map()as an internal compiled index
field_map() should walk explicit object properties and return StateFieldDecl values keyed by rootless state path. It must:
-
include every explicit property path
-
parse
reducerwithReducerRef -
default missing reducer to
wf.std.replace -
preserve
traceanddefaultworkflow extension keywords -
remove workflow extension keywords from
StateFieldDecl.validation_schema -
Step 4: Validate JSON Schema and extension keyword types
Use SchemaRef/jsonschema validation for the complete state schema. Add explicit validation that:
reduceris a string orReducerRef-compatible objecttraceis a boolean when presentdefaultis allowed as JSON Schema/default metadata
Task 3: Update Artifact Reducer Extraction
Files:
-
Modify:
src/wf_artifacts/factory.py -
Step 1: Extract reducer dependencies from
state_schema.properties
Add a helper that walks explicit JSON Schema properties and yields reducer payloads from every property schema.
- Step 2: Keep legacy
fieldsextraction only as compatibility
If state_schema.fields exists in old artifacts, continue reading it. Prefer canonical properties when present.
- Step 3: Add tests through existing workflow surface/artifact tests
Use an existing artifact/dependency test and assert a reducer declared at:
state_schema.properties.count.reducer
is included in required capabilities.
Task 4: Update Authoring Conversion
Files:
-
Modify:
src/wf_authoring/schemas.py -
Modify:
tests/authoring/test_schemas.py -
Step 1: Attach reducer metadata directly to generated property schemas
When state_schema_from(BaseModel) sees Annotated[..., state_field(reducer=...)], inject reducer and trace into that property schema instead of building a separate field map.
- Step 2: Preserve model JSON Schema as the state schema
Return StateSchema.model_validate(schema_with_reducer_keywords) so generated state schema remains JSON Schema-shaped.
Task 5: Update Docs and Examples
Files:
-
Modify:
docs/core_state_mapping_and_merge.md -
Modify:
docs/workflow_drafts.md -
Modify:
docs/wf_mcp_operator_manual.md -
Modify:
docs/wf_mcp_end_to_end_runbook.md -
Modify:
examples/raw_canonical_workflow.py -
Step 1: Replace canonical
fields: [...]examples
Use JSON Schema:
{
"type": "object",
"properties": {
"count": {
"type": "integer",
"description": "Counter value",
"reducer": "wf.std.add"
}
}
}
- Step 2: Document extension semantics
State clearly that reducer is not standard JSON Schema behavior. JSON Schema validators ignore it; wf_core reads it for workflow state writes.
Task 6: Verification
Files:
-
All touched files
-
Step 1: Run focused tests
Run: uv run --with pytest pytest tests/core/test_nested_state_paths.py tests/core/test_schema_validation.py tests/authoring/test_schemas.py -q
- Step 2: Run full tests
Run: uv run --with pytest pytest -q
- Step 3: Run static checks
Run:
uvx ruff check
uv run basedpyright --level error
Self-Review
- Spec coverage: covers canonical JSON Schema state shape, reducer extension keyword, compatibility, runtime lookup, artifact dependency extraction, authoring generation, docs, and verification.
- Placeholder scan: no placeholders remain.
- Type consistency:
StateSchema,StateFieldDecl,ReducerRef, andSchemaRefnames match current code.