11 KiB
Nested Local Draft Bindings Design
Goal
Bring focused draft authoring into parity with the canonical runtime model for nested node-local paths. Agents and operators should be able to bind structured capability inputs and outputs without raw JSON Patch, while the implementation reuses canonical path types and one shared JSON Schema projection module.
Problem
LocalPath, workflow validation, runtime input assembly, runtime output reads,
and WorkflowBuilder already support paths such as report.title. Focused
draft authoring is narrower:
wf draft bind --from input.title --to local.report.titlerejects the target becausebind_draftrequires one local path segment;- the inverse output direction rejects nested local sources for the same reason;
- capability-step insertion persists nested input targets but silently skips schema projection whenever the local path has more than one segment;
- CLI help describes step input targets as bare fields rather than canonical node-local paths.
The limitation is not in the workflow model. It is duplicated path and schema handling in the focused authoring layer.
Scope
This slice supports nested node-local paths for:
- workflow input or state to capability input:
input.title -> local.report.title; - capability output to workflow state:
local.report.markdown -> state.report.markdown; - capability output to public workflow output, using the existing durable-state
lowering:
local.report.markdown -> output.report.markdownbecomes a node output binding tostate.report.markdownplus a workflow output binding from that state path; - capability-step input maps such as
--map input.title=report.title, where the node-local root remains implied by the existing command interface.
The slice also centralizes repeated authoring-time JSON Schema path operations and updates CLI/agent-facing path documentation.
Out Of Scope
This slice does not:
- add literal node-input or workflow-output bindings;
- replace dictionary maps with a fan-out-safe binding-list interface;
- add the atomic structured-input assembly helper tracked separately in
ISSUES.md; - add step metadata or focused update-step operations;
- add TypeScript JSON-RPC operations or code generation;
- change route, revision, deployment, or runtime semantics;
- implement a general JSON Schema resolver or support remote references;
- change existing CLI command names or request fields.
Canonical Path Interfaces
No new path model is introduced.
GraphSourcePathparses workflow-readableinput.*,state.*, andcontext.*paths.LocalPathparses rootless node-local paths such asreport.titleand the whole-payload marker..wf draft bindcontinues using explicit endpoint roots because both sides are endpoints:--from input.title --to local.report.title.- capability-add and set-input maps continue using rootless local targets
because their interface already implies the node-local side:
--map input.title=report.title.
The CLI parser must validate rootless map targets with LocalPath.parse instead
of checking only whether they start with local.. Existing single-segment map
syntax remains valid. Rooted local.* map targets remain rejected with an exact
repair example because changing that syntax is a separate migration.
Transport request fields remain strings. Their endpoint role is determined by the existing command/method interface, and the semantic authoring module parses them through the canonical path classes. Do not create duplicate Pydantic path schemas or endpoint unions in RPC models for this slice.
Shared JSON Schema Projection Module
Deepen wf_api.schema_projection so authoring code does not perform schema
traversal itself.
Public Operations
The module exposes:
def schema_path_exists(
schema: Mapping[str, Any],
parts: Sequence[str],
) -> bool: ...
def project_schema_path_to_schema_path(
*,
target_schema: JsonObject,
source_schema: JsonObject,
source_parts: tuple[str, ...],
target_parts: tuple[str, ...],
allow_existing_equivalent: bool = False,
) -> JsonObject: ...
project_property_to_schema_path remains as a compatibility-preserving
root-property wrapper and delegates to the new operation with
source_parts=(source_field,). Existing callers and error wording remain
stable where practical.
Responsibilities
The module:
- validates source and target documents with
jsonschema; - traverses nested object
propertiesusing path segments supplied by canonical path types; - resolves bounded local references used by repository-generated schemas,
including
#/$defs/...and#/definitions/...; - copies the selected source subschema into the requested target object path;
- carries
$defsanddefinitionsblocks needed by copied local references; - validates the projected result;
- reports missing source paths, non-object intermediate paths, unsupported references, and conflicting target paths precisely.
This is authoring-time schema projection, not schema inference. The module does
not create new Pydantic models mirroring capability schemas and does not
evaluate arbitrary combinators or remote references. Capability schemas remain
the documents emitted by NodeSpec contracts or Pydantic
model_json_schema().
The duplicate _schema_path_exists implementations in wf_api.drafts and
wf_api.draft_authoring move into this module. Both callers import the shared
operation so target-path behavior has one implementation.
Authoring Data Flow
Focused Bind: Workflow Data To Nested Capability Input
For input.title -> local.report.title:
- revision preflight runs before semantic work;
GraphSourcePathparsesinput.title;LocalPathparses rootlessreport.titlefrom the explicitlocal.*endpoint;- the capability input schema is selected from its explicit contract or Pydantic model;
project_schema_path_to_schema_pathcopies the capability schema atreport.titleinto the workflow input schema attitlewhen absent;input_bindings_payloadserializes the canonical nested local target;- the existing revision-checked patch operation persists both changes.
Existing workflow source schema paths are reused without projection, preserving current idempotent behavior.
Focused Bind: Nested Capability Output To State Or Output
For local.report.markdown -> state.report.markdown, the capability output
schema at report.markdown is copied to the requested state path and the step
output binding stores the complete nested LocalPath.
For local.report.markdown -> output.report.markdown, the existing output
lowering remains unchanged except that the nested capability subschema is used.
The operation projects that subschema into both state and public output schemas,
writes the node output into state, and projects workflow output from the same
state path.
Capability-Step Insertion
For input.title=report.title:
GraphSourcePath.parsevalidates the graph source;LocalPath.parsevalidates the rootless local target;- the complete local parts tuple selects the nested capability input subschema;
- that subschema is projected into the workflow input/state source path when the workflow schema does not already declare it;
- the nested input binding is persisted in the same atomic patch as the new step and routes.
Remove the current len(local_parts) != 1 skip. A valid nested local path must
never silently disable schema projection.
Errors And Compatibility
Current single-field bindings keep their response shapes and semantics.
For current revisions:
- an absent nested capability schema path raises a precise
ValueErrornaming the complete path; - traversing through a scalar or otherwise non-object schema raises a precise
ValueErrornaming the blocking prefix; - an unsupported or unresolved reference raises a precise
ValueErrorrather than silently skipping projection; - an existing workflow input/state source path is reused unchanged, preserving current bind behavior;
- output/state projection continues accepting exact equivalent target schemas and rejecting incompatible existing targets;
- no patch is persisted when projection fails.
Revision precedence from the preceding slice remains intact: after intrinsic
request validation, stale requests return revision_conflict before path or
schema errors derived from current workspace/catalog state.
Transport fields and response envelopes do not change. RPC, remote-client, and CLI layers inherit behavior through existing delegation. Documentation-only description changes may clarify nested examples but must not introduce parallel request schemas.
Testing
Schema Projection
Add focused unit tests for:
- inline nested source properties;
- nested source properties behind Pydantic-style
#/$defsreferences; - legacy
#/definitionslocal references; - copied definition blocks remaining resolvable;
- missing source paths;
- non-object intermediate source paths;
- conflicting and equivalent target paths;
- centralized
schema_path_existsbehavior.
Python Authoring
Add API tests for:
- input and state binding to nested local targets;
- nested local output binding to nested state;
- nested local output binding to public output through state;
- capability-step insertion with nested input target and missing workflow schema projection;
- current-revision invalid nested capability paths producing clear errors and no mutation;
- stale revision winning over nested-path semantic errors;
- existing single-segment and idempotent cases remaining unchanged.
Transport And CLI
Add delegation/regression tests proving:
- JSON-RPC and the remote client preserve nested path strings unchanged;
wf draft bindaccepts explicit rooted nested local endpoints;- capability-add and set-input map parsing accept rootless nested local paths;
- rooted
local.*map targets remain rejected with a repair example; - help and agent instructions distinguish explicit bind endpoints from implied local map targets.
Compile or validate the resulting draft to prove the stored canonical bindings
are accepted by the existing workflow model. Runtime nested mapping behavior is
already covered in tests/core/test_nested_mappings.py and should not be
reimplemented in this slice.
Documentation And Issue State
After verification:
- check the nested
wf draft bindlimitation; - check the capability-step nested projection limitation;
- check the CLI help/agent instruction limitation;
- leave atomic structured-input assembly, literal bindings, fan-out maps, nested workflow-output source projection, step metadata, and TypeScript parity open;
- add a completed roadmap entry linking to the archived implementation plan;
- archive the implementation plan under
docs/historical/superpowers/plans/.
Success Criteria
- Focused bind supports nested
LocalPathvalues in both input and output directions. - Capability-step insertion projects schemas for nested local input targets instead of silently skipping them.
- Existing single-segment bind and map behavior remains compatible.
- No new transport request schema or duplicate path type is introduced.
- Common schema path lookup, local-reference handling, existence checks, and
projection live in
wf_api.schema_projection. - Resulting drafts compile or validate through the existing canonical workflow model.
- Focused API/RPC/CLI tests, Ruff, formatting, basedpyright, and relevant core regressions pass.