12 KiB
Atomic Step Input Bindings Design
Goal
Expose the canonical step input-binding list through focused draft authoring so an agent or operator can assemble one structured node input from graph paths and literal values in one revision-checked edit.
Problem
The runtime and persisted workflow model already represent node input assembly
as an ordered list of InputPathBinding | InputValueBinding. Each binding writes
one graph-sourced or literal value to a node-local path, and several bindings can
therefore build one nested input object.
Focused draft authoring narrows this list into a source-to-target dictionary. That surface cannot express literal values or one source feeding several local targets. Building a structured input across several focused bind commands also advances the draft revision after every field and can leave an incomplete authoring state between commands.
The missing concept is not a new runtime binding. It is one atomic authoring operation over the canonical binding list.
Terms
- Step input binding is the existing canonical
InputPathBindingorInputValueBindingstored on a node-like step. - Structured input assembly is the runtime result of applying several step input bindings to nested node-local targets.
- Binding replacement replaces the complete input-binding list for one step. It does not merge bindings by source or target.
Do not introduce a CompositeBinding model. Composition is behavior produced
by the existing binding list, not a separate persisted domain object.
Scope
This slice adds an atomic, focused operation for replacing the complete input bindings of one capability-backed draft step. It supports:
- several graph sources assembling one nested node-local object;
- one graph source feeding several distinct node-local targets;
- literal JSON values, including explicit
null; - nested and whole-payload node-local targets;
- authoring-time workflow input/state schema projection from the capability input contract;
- local API, JSON-RPC, remote client, MCP, and CLI access.
The stored workflow shape, core runtime behavior, and canonical path models do not change.
Out Of Scope
This slice does not:
- add incremental add/update/remove semantics to the canonical operation;
- add revision amend, rebase, fast-forward, or history semantics;
- change step output bindings or workflow output bindings;
- solve output fan-out or literal workflow outputs;
- add focused step metadata or update-step operations;
- add TypeScript JSON-RPC parity or code generation;
- introduce a structured template language such as
$pathexpressions; - implement general JSON Schema reference resolution or schema assignability.
Canonical Interface
Add the following method to WorkflowApiSurface and its local implementation:
async def set_step_input_bindings(
*,
workspace_id: str,
revision: int,
step_id: str,
bindings: Sequence[InputBinding],
) -> dict[str, Any]: ...
Expose the same request through:
- JSON-RPC method
workflow.draft_workspaces.set_step_input_bindings; RpcWorkflowApiClient;- the MCP workflow surface.
Transport models reuse wf_core.models.steps.InputBinding. They must not define
parallel path/value binding models.
The operation replaces the complete ordered list. It preserves caller order in the stored draft. Repeated graph source paths are valid when their local targets are distinct. Equal or ancestor/descendant local targets overlap and are rejected, regardless of list order.
Example request bindings:
[
{"path": "state.report.title", "target": "request.title"},
{"path": "state.report.markdown", "target": "request.body"},
{"value": "markdown", "target": "request.format"}
]
Revision And Mutation Semantics
The operation follows the established semantic-authoring precedence:
- Validate the request envelope and canonical binding shapes.
- Load the workspace and compare the expected revision.
- Return the canonical
revision_conflictresult immediately when stale. - Resolve the current step and capability contract.
- Validate binding semantics and compute schema projections.
- Replace the binding list and projected schemas through one JSON Patch call.
- Let the mutation-time revision guard protect against races after preflight.
No partial patch is persisted when any binding fails. A changed replacement creates exactly one new draft revision. After revision and semantic validation, an exact replacement whose canonical bindings and projected schemas equal the stored values returns the current workspace summary without advancing the revision.
Semantic Validation
The authoring operation resolves the capability-backed step and validates the complete replacement before mutation.
Local Targets
Every binding target must resolve against the capability input schema. Nested
targets may traverse bounded local #/$defs/... or #/definitions/...
references. The whole-payload target . addresses the complete capability
input schema.
Targets must not overlap. For example, request and request.title cannot
appear together because the runtime would otherwise make the result depend on
write order. Two bindings with the same target are also overlapping. The same
source may feed request.title and audit.title.
Path Bindings
InputPathBinding.path continues accepting canonical workflow-readable
input.*, state.*, and context.* paths.
For input.* and state.* sources:
- reuse an already-declared workflow schema path;
- when the source path is missing, project its schema from the corresponding capability-input target schema;
- apply every projection to staged schemas and commit them with the binding replacement.
Existing source schemas are not subjected to a new assignability algorithm.
They retain current bind behavior and remain subject to draft/runtime
validation. context.* bindings are valid but do not project workflow schemas.
Literal Bindings
InputValueBinding.value accepts any JSON-compatible value, including null,
arrays, and objects. Validate a literal against the capability schema selected
by its local target before mutation. A literal targeting . must be an object
that satisfies the complete capability input schema because the runtime whole
payload operation requires a mapping.
Shared JSON Schema Operations
Deepen wf_api.schema_projection rather than adding traversal to draft
authoring. Add one reusable schema-path selection/validation operation that:
- selects inline nested schemas;
- follows bounded local
$defsand legacydefinitionsreferences; - retains enough root definition context to validate selected fragments;
- rejects remote, unresolved, cyclic, and non-object intermediate references precisely;
- supports the existing projection helpers and literal validation.
The exact private/public helper split is an implementation choice, but schema
path traversal, local-reference handling, projection, and literal validation
must remain localized in wf_api.schema_projection.
CLI Design
Keep the existing command name:
wf draft set-input WORKSPACE --revision N --step STEP ...
Preferred canonical replacement forms are:
wf draft set-input WS \
--revision 4 \
--step publish \
--map state.report.title=request.title \
--map state.report.markdown=request.body \
--value request.format='"markdown"'
and:
wf draft set-input WS \
--revision 4 \
--step publish \
--bindings-file bindings.json
CLI behavior:
- repeatable
--map SOURCE=LOCAL_TARGETcreates path bindings; - repeatable
--value LOCAL_TARGET=JSONcreates literal bindings; --bindings-fileaccepts the exact canonical JSON array;--bindings-fileis mutually exclusive with--mapand--value;--clearexplicitly replaces the list with[];- no bindings and no
--clearis an input error; - the canonical replacement path does not support
--merge.
--bindings-file preserves exact list order. Convenience flags serialize path
bindings in --map order followed by literal bindings in --value order.
Because overlapping targets are rejected, this cross-kind ordering cannot
change the assembled payload.
The existing map-only API/RPC operation and its merge behavior remain as a
compatibility adapter for real callers. Existing CLI --merge may continue to
delegate to that adapter only for map-only requests; it must reject combinations
with --value, --bindings-file, or --clear. New help and agent instructions
describe replacement as the preferred behavior.
Current bindings remain exportable without another endpoint:
wf draft inspect WS --include-draft |
jq '.draft.steps.publish.input' > bindings.json
Error Contract
Semantic errors identify the binding index and complete canonical path where possible. Representative messages are:
bindings[1].target 'request.missing' is not declared by capability ...;bindings[0].target 'request' overlaps bindings[1].target 'request.title';bindings[2].value does not satisfy the schema at 'request.format';bindings[0].path 'input.report.title' cannot be projected from target ....
Malformed canonical binding objects fail request-envelope validation. After that validation, stale revision wins over missing steps, unknown capabilities, schema lookup, overlap, and literal-value errors. All failures leave the draft document and revision unchanged.
Testing
Schema Projection
Add focused tests for:
- selecting inline and nested schema paths;
- selecting paths through
$defsanddefinitionsreferences; - preserving definition context during literal validation;
- valid and invalid literal scalars, objects, arrays, and explicit
null; - remote, unresolved, cyclic, and non-object intermediate references.
Python Authoring
Add API tests for:
- several path bindings assembling one nested capability input;
- path and literal bindings in one replacement;
- one source fanning out to several targets;
- explicit
nulland whole-payload bindings; - stable binding order in the stored draft;
- input and state schema projection across several bindings;
- context bindings without schema projection;
- missing, duplicate, and ancestor/descendant targets;
- invalid literals;
- stale revision precedence and no mutation on every failure class;
- replacement rather than merge semantics;
- compatibility of the existing map-only operation.
Compile the resulting draft and execute one representative workflow to prove the runtime receives the intended nested input object.
Transport And CLI
Add tests proving:
- JSON-RPC, remote client, and MCP preserve canonical path/value unions and binding order;
- CLI
--mapand--valuecompose one replacement request; --bindings-fileround-trips an exported inspected list;--clearis explicit and an empty accidental invocation fails;- canonical replacement rejects
--mergecombinations; - the legacy map-only merge path remains operational;
- expected errors remain compact through local and remote CLI targets.
Documentation And Issue State
After verification:
- check the atomic structured node-input assembly issue;
- check the focused literal node-input binding issue;
- leave input/output fan-out map loss open unless the implementation also migrates every lossy reader and writer to canonical lists;
- leave workflow-output literals, nested workflow-output projection, focused step updates, and TypeScript parity open;
- update
wf_cliand agent skill documentation with replacement, literal, and export examples; - add a completed roadmap entry linking to the archived implementation plan;
- archive the implementation plan under
docs/historical/superpowers/plans/.
Success Criteria
- One focused operation replaces a step's complete canonical input-binding list in one revision.
- Structured nested assembly, path fan-out, literal values, explicit
null, and whole-payload targets use the existing runtime model unchanged. - All local targets are validated against the capability input schema before mutation.
- Missing input/state source schemas are projected atomically with the binding replacement.
- Existing map-only callers retain their current behavior through a compatibility adapter, while new callers use canonical bindings.
- No new persisted binding type or template language is introduced.
- Focused API/RPC/MCP/CLI tests, Ruff, formatting, basedpyright, compile, and runtime regressions pass.