docs: complete atomic workflow output bindings

This commit is contained in:
lda
2026-07-26 21:35:02 +07:00 Verified
parent b0a01a63b0
commit b65f11e2da
10 changed files with 168 additions and 87 deletions
+5 -5
View File
@@ -23,11 +23,11 @@
- [ ] Compatibility step input/output maps can still collapse valid canonical - [ ] Compatibility step input/output maps can still collapse valid canonical
fan-out bindings. Canonical input and output replacement preserve ordered fan-out bindings. Canonical input and output replacement preserve ordered
fan-out, but later compatibility-map merges remain inherently lossy. fan-out, but later compatibility-map merges remain inherently lossy.
- [ ] Focused workflow-output authoring cannot add or update literal output - [x] Focused workflow-output authoring supports literal output bindings through
bindings even though `WorkflowDraft.output` accepts canonical value bindings. canonical Python, JSON-RPC, MCP, and CLI replacement surfaces.
- [ ] Workflow output schema projection skips nested sources such as - [x] Workflow-output replacement projects nested `input.*` and `state.*`
`state.report.title`, leaving callers to patch the output schema manually even source schemas, including local references, into missing nested public output
when the nested source schema is already declared. targets without requiring raw schema patches.
- [x] CLI help and agent instructions describe step-input targets as bare local - [x] CLI help and agent instructions describe step-input targets as bare local
fields and do not document the nested composition behavior already supported fields and do not document the nested composition behavior already supported
by the canonical runtime model. by the canonical runtime model.
+9 -2
View File
@@ -590,9 +590,16 @@ clear operator feedback before adding more architecture.
Implementation: Implementation:
[`draft remove commands`](historical/superpowers/plans/2026-06-28-draft-remove-commands.md). [`draft remove commands`](historical/superpowers/plans/2026-06-28-draft-remove-commands.md).
- Completed: `wf draft set-workflow-output` and full-stack API/RPC/CLI support - Completed: `wf draft set-workflow-output` and full-stack API/RPC/CLI support
for editing top-level workflow output bindings. Accepts repeatable `--map` for editing top-level workflow output bindings. The compatibility map
and `--merge` flag. Implementation: adapter remains available for existing callers. Implementation:
[`set-workflow-output API/RPC/CLI`](historical/superpowers/plans/2026-06-29-set-workflow-output.md). [`set-workflow-output API/RPC/CLI`](historical/superpowers/plans/2026-06-29-set-workflow-output.md).
- Completed: canonical workflow-output replacement now preserves ordered
path/value bindings across Python, JSON-RPC, MCP, and CLI; nested input/state
sources can project missing output schemas, while literals and `context.*`
require declared targets. Empty replacement restores implicit same-name state
fallback; compatibility `--merge --map` remains intentionally lossy.
Implementation:
[`atomic workflow output bindings`](historical/superpowers/plans/2026-07-23-atomic-workflow-output-bindings.md).
- Completed: challenge-driven output UX polish makes `set-workflow-output` - Completed: challenge-driven output UX polish makes `set-workflow-output`
project missing top-level output schema fields from declared `input.*` and project missing top-level output schema fields from declared `input.*` and
`state.*` sources, and challenge prompt templates now always include `state.*` sources, and challenge prompt templates now always include
@@ -1,6 +1,6 @@
# Atomic Workflow Output Bindings Implementation Plan # Atomic Workflow Output Bindings 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. > **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 (`- [x]`) syntax for tracking.
**Goal:** Add one revision-checked canonical workflow-output binding replacement operation across Python, JSON-RPC, MCP, and CLI, with nested schema projection, literal validation, and unchanged empty-list fallback semantics. **Goal:** Add one revision-checked canonical workflow-output binding replacement operation across Python, JSON-RPC, MCP, and CLI, with nested schema projection, literal validation, and unchanged empty-list fallback semantics.
@@ -36,7 +36,7 @@
- Produces: `WorkflowDraftAuthoringApi.set_workflow_output_bindings(*, workspace_id: str, revision: int, bindings: Sequence[InputBinding]) -> dict[str, Any]`. - Produces: `WorkflowDraftAuthoringApi.set_workflow_output_bindings(*, workspace_id: str, revision: int, bindings: Sequence[InputBinding]) -> dict[str, Any]`.
- Produces: `validate_json_value_at_schema_path(..., schema_label: str = "capability input schema") -> None`, preserving existing callers through the default. - Produces: `validate_json_value_at_schema_path(..., schema_label: str = "capability input schema") -> None`, preserving existing callers through the default.
- [ ] **Step 1: Write failing neutral-label and referenced-target schema tests** - [x] **Step 1: Write failing neutral-label and referenced-target schema tests**
Add tests in `tests/wf_api/test_schema_projection.py` that pin the shared helper contract: Add tests in `tests/wf_api/test_schema_projection.py` that pin the shared helper contract:
@@ -84,7 +84,7 @@ def test_project_schema_path_updates_output_target_through_local_ref() -> None:
The second test protects the target-side `$ref` behavior introduced by the step-output slice while this operation begins relying on it directly. The second test protects the target-side `$ref` behavior introduced by the step-output slice while this operation begins relying on it directly.
- [ ] **Step 2: Run the schema tests and verify the neutral label test fails** - [x] **Step 2: Run the schema tests and verify the neutral label test fails**
Run: Run:
@@ -95,7 +95,7 @@ uv run pytest tests/wf_api/test_schema_projection.py -q
Expected: the existing referenced-target test passes and the new Expected: the existing referenced-target test passes and the new
`schema_label` call fails because the helper does not yet accept that keyword. `schema_label` call fails because the helper does not yet accept that keyword.
- [ ] **Step 3: Generalize the literal-validation diagnostic label** - [x] **Step 3: Generalize the literal-validation diagnostic label**
Change `validate_json_value_at_schema_path(...)` in Change `validate_json_value_at_schema_path(...)` in
`src/wf_api/schema_projection.py` without adding a second validator: `src/wf_api/schema_projection.py` without adding a second validator:
@@ -126,7 +126,7 @@ def validate_json_value_at_schema_path(
Run the schema test file again. Expected: PASS. Run the schema test file again. Expected: PASS.
- [ ] **Step 4: Write failing canonical authoring tests** - [x] **Step 4: Write failing canonical authoring tests**
In `tests/wf_api/test_drafts_service.py`, add a helper draft whose input, state, In `tests/wf_api/test_drafts_service.py`, add a helper draft whose input, state,
and output schemas contain nested fields and whose output list initially is and output schemas contain nested fields and whose output list initially is
@@ -262,7 +262,7 @@ async def test_set_workflow_output_bindings_rejects_without_mutation(
# assert the complete inspected workspace still equals the snapshot. # assert the complete inspected workspace still equals the snapshot.
``` ```
- [ ] **Step 5: Run focused authoring tests and verify they fail** - [x] **Step 5: Run focused authoring tests and verify they fail**
Run: Run:
@@ -273,7 +273,7 @@ uv run pytest tests/wf_api/test_drafts_service.py -q -k "workflow_output_binding
Expected: FAIL because `WorkflowDraftAuthoringApi` has no canonical workflow Expected: FAIL because `WorkflowDraftAuthoringApi` has no canonical workflow
output method. output method.
- [ ] **Step 6: Implement the canonical authoring operation** - [x] **Step 6: Implement the canonical authoring operation**
In `src/wf_api/draft_authoring.py`, import `Mapping`, `InputBinding`, In `src/wf_api/draft_authoring.py`, import `Mapping`, `InputBinding`,
`InputPathBinding`, `InputValueBinding`, `GraphSourcePath`, `LocalPath`, and the `InputPathBinding`, `InputValueBinding`, `GraphSourcePath`, `LocalPath`, and the
@@ -422,7 +422,7 @@ Do not copy this mechanically if an existing helper provides the same behavior;
reuse it. Preserve the specified validation order and wrap projection failures reuse it. Preserve the specified validation order and wrap projection failures
with `bindings[index]` context. with `bindings[index]` context.
- [ ] **Step 7: Add compile-and-execute coverage** - [x] **Step 7: Add compile-and-execute coverage**
Add a test that creates a valid draft, invokes the new authoring method, compiles Add a test that creates a valid draft, invokes the new authoring method, compiles
with `compile_draft_workspace`, runs through `WfMcpService.run_workflow_from_plan`, with `compile_draft_workspace`, runs through `WfMcpService.run_workflow_from_plan`,
@@ -441,7 +441,7 @@ Add a second execution assertion for a cleared explicit list: declare a
top-level output field matching state, clear bindings, compile/run, and prove the top-level output field matching state, clear bindings, compile/run, and prove the
existing implicit fallback still returns that state field. existing implicit fallback still returns that state field.
- [ ] **Step 8: Run Task 1 verification** - [x] **Step 8: Run Task 1 verification**
Run: Run:
@@ -454,7 +454,7 @@ uv run basedpyright --level error src/wf_api/schema_projection.py src/wf_api/dra
Expected: all pass. If the installed basedpyright does not accept file paths, Expected: all pass. If the installed basedpyright does not accept file paths,
run the repository-wide `uv run basedpyright --level error` instead. run the repository-wide `uv run basedpyright --level error` instead.
- [ ] **Step 9: Review and commit Task 1** - [x] **Step 9: Review and commit Task 1**
Review the Task 1 diff against the spec, especially root-target equality, Review the Task 1 diff against the spec, especially root-target equality,
context behavior, stale-revision precedence, and no mutation on errors. Then: context behavior, stale-revision precedence, and no mutation on errors. Then:
@@ -482,7 +482,7 @@ git commit -m "feat: replace canonical workflow output bindings"
- Consumes: Task 1's `WorkflowDraftAuthoringApi.set_workflow_output_bindings(...)`. - Consumes: Task 1's `WorkflowDraftAuthoringApi.set_workflow_output_bindings(...)`.
- Produces: `WorkflowApiSurface.set_workflow_output_bindings(...)`, `WorkflowApi.set_workflow_output_bindings(...)`, `SetWorkflowOutputBindingsParams`, JSON-RPC method `workflow.draft_workspaces.set_workflow_output_bindings`, and `RpcWorkflowApiClient.set_workflow_output_bindings(...)`. - Produces: `WorkflowApiSurface.set_workflow_output_bindings(...)`, `WorkflowApi.set_workflow_output_bindings(...)`, `SetWorkflowOutputBindingsParams`, JSON-RPC method `workflow.draft_workspaces.set_workflow_output_bindings`, and `RpcWorkflowApiClient.set_workflow_output_bindings(...)`.
- [ ] **Step 1: Write failing RPC model and endpoint tests** - [x] **Step 1: Write failing RPC model and endpoint tests**
In `tests/wf_transport_rpc_http/test_app.py`, add an end-to-end request using a In `tests/wf_transport_rpc_http/test_app.py`, add an end-to-end request using a
real local server: real local server:
@@ -528,7 +528,7 @@ stored list preserves a path binding followed by a value binding. Add malformed
requests proving `path` plus `value`, missing `target`, extra fields, and invalid requests proving `path` plus `value`, missing `target`, extra fields, and invalid
roots fail with JSON-RPC `-32602` before semantic logic. roots fail with JSON-RPC `-32602` before semantic logic.
- [ ] **Step 2: Write the failing remote client test** - [x] **Step 2: Write the failing remote client test**
In `tests/wf_transport_rpc_http/test_client.py`, add: In `tests/wf_transport_rpc_http/test_client.py`, add:
@@ -566,7 +566,7 @@ async def test_rpc_client_set_workflow_output_bindings_preserves_union_order(
Adapt the fixture names to the existing client test harness; keep the assertions Adapt the fixture names to the existing client test harness; keep the assertions
field-level. field-level.
- [ ] **Step 3: Run RPC tests and verify they fail** - [x] **Step 3: Run RPC tests and verify they fail**
Run: Run:
@@ -576,7 +576,7 @@ uv run pytest tests/wf_transport_rpc_http/test_app.py tests/wf_transport_rpc_htt
Expected: FAIL because the params model, endpoint, and client method are absent. Expected: FAIL because the params model, endpoint, and client method are absent.
- [ ] **Step 4: Add the public surface and local service delegation** - [x] **Step 4: Add the public surface and local service delegation**
Add this method to `WorkflowApiSurface` in `src/wf_api/surface.py`: Add this method to `WorkflowApiSurface` in `src/wf_api/surface.py`:
@@ -610,7 +610,7 @@ async def set_workflow_output_bindings(
Keep `set_workflow_output_map(...)` unchanged and adjacent as the compatibility Keep `set_workflow_output_map(...)` unchanged and adjacent as the compatibility
surface. surface.
- [ ] **Step 5: Add the typed RPC request, endpoint, and client** - [x] **Step 5: Add the typed RPC request, endpoint, and client**
In `src/wf_transport_rpc_http/models.py`: In `src/wf_transport_rpc_http/models.py`:
@@ -667,7 +667,7 @@ async def set_workflow_output_bindings(
Export `SetWorkflowOutputBindingsParams` from Export `SetWorkflowOutputBindingsParams` from
`src/wf_transport_rpc_http/__init__.py` in both import and `__all__` lists. `src/wf_transport_rpc_http/__init__.py` in both import and `__all__` lists.
- [ ] **Step 6: Run Task 2 verification** - [x] **Step 6: Run Task 2 verification**
Run: Run:
@@ -678,7 +678,7 @@ uv run basedpyright --level error
Expected: all pass and every `WorkflowApiSurface` implementation conforms. Expected: all pass and every `WorkflowApiSurface` implementation conforms.
- [ ] **Step 7: Review and commit Task 2** - [x] **Step 7: Review and commit Task 2**
Review request validation, canonical order, and local/remote interface parity. Review request validation, canonical order, and local/remote interface parity.
Then: Then:
@@ -704,7 +704,7 @@ git commit -m "feat: expose workflow output bindings over rpc"
- Consumes: Task 2's public `set_workflow_output_bindings(...)` method and the existing `DraftInputBindings` alias. - Consumes: Task 2's public `set_workflow_output_bindings(...)` method and the existing `DraftInputBindings` alias.
- Produces: `SetWorkflowOutputBindingsRequest` and MCP tool `wf.workflow.set_workflow_output_bindings`. - Produces: `SetWorkflowOutputBindingsRequest` and MCP tool `wf.workflow.set_workflow_output_bindings`.
- [ ] **Step 1: Write failing MCP request-model tests** - [x] **Step 1: Write failing MCP request-model tests**
In `tests/wf_mcp/workflow_surface/test_drafts.py`, add: In `tests/wf_mcp/workflow_surface/test_drafts.py`, add:
@@ -730,7 +730,7 @@ def test_set_workflow_output_bindings_request_preserves_union_order() -> None:
Add a malformed-record test with both `path` and `value`, and assert Pydantic Add a malformed-record test with both `path` and `value`, and assert Pydantic
rejects the extra union field. rejects the extra union field.
- [ ] **Step 2: Write failing tool-registration and invocation tests** - [x] **Step 2: Write failing tool-registration and invocation tests**
Extend the fake handler in `tests/wf_mcp/server/test_tools.py` with a call log for Extend the fake handler in `tests/wf_mcp/server/test_tools.py` with a call log for
`set_workflow_output_bindings`. Assert the tool inventory includes the new name, `set_workflow_output_bindings`. Assert the tool inventory includes the new name,
@@ -760,7 +760,7 @@ assert handler.calls[-1]["bindings"][1].value == "markdown"
Update config/discovery expectations so the public tool count/name inventory is Update config/discovery expectations so the public tool count/name inventory is
explicit rather than only asserting a count. explicit rather than only asserting a count.
- [ ] **Step 3: Run MCP tests and verify they fail** - [x] **Step 3: Run MCP tests and verify they fail**
Run: Run:
@@ -770,7 +770,7 @@ uv run pytest tests/wf_mcp/workflow_surface/test_drafts.py tests/wf_mcp/server/t
Expected: FAIL because the request and tool do not exist. Expected: FAIL because the request and tool do not exist.
- [ ] **Step 4: Add the MCP request and tool** - [x] **Step 4: Add the MCP request and tool**
In `src/wf_mcp/workflow_surface/models.py`: In `src/wf_mcp/workflow_surface/models.py`:
@@ -809,7 +809,7 @@ async def set_workflow_output_bindings(
Add the new name to the explicit workflow proxy allow-list in Add the new name to the explicit workflow proxy allow-list in
`src/wf_mcp/proxy/runtime.py`. Preserve the existing map tool. `src/wf_mcp/proxy/runtime.py`. Preserve the existing map tool.
- [ ] **Step 5: Run Task 3 verification** - [x] **Step 5: Run Task 3 verification**
Run: Run:
@@ -820,7 +820,7 @@ uv run basedpyright --level error
Expected: all pass. Expected: all pass.
- [ ] **Step 6: Review and commit Task 3** - [x] **Step 6: Review and commit Task 3**
Review actual tool invocation, discovery visibility, request validation, and Review actual tool invocation, discovery visibility, request validation, and
ordered union preservation. Then: ordered union preservation. Then:
@@ -844,7 +844,7 @@ git commit -m "feat: expose workflow output bindings to mcp"
- Consumes: Task 2's local/remote `set_workflow_output_bindings(...)` method. - Consumes: Task 2's local/remote `set_workflow_output_bindings(...)` method.
- Produces: canonical `wf draft set-workflow-output` modes `--map`, `--value`, `--bindings-file`, and `--clear`; preserves compatibility-only `--merge --map`. - Produces: canonical `wf draft set-workflow-output` modes `--map`, `--value`, `--bindings-file`, and `--clear`; preserves compatibility-only `--merge --map`.
- [ ] **Step 1: Write failing parser tests** - [x] **Step 1: Write failing parser tests**
In `tests/wf_cli/test_app.py`, add parser tests proving: In `tests/wf_cli/test_app.py`, add parser tests proving:
@@ -874,7 +874,7 @@ Add bindings-file tests for a mixed canonical array and malformed unions. Pin
compact `typer.BadParameter` messages for invalid graph roots, `local.`-prefixed compact `typer.BadParameter` messages for invalid graph roots, `local.`-prefixed
targets, invalid JSON, and non-array files. targets, invalid JSON, and non-array files.
- [ ] **Step 2: Extract shared input-shaped CLI parsers** - [x] **Step 2: Extract shared input-shaped CLI parsers**
In `src/wf_cli/commands/draft_options.py`, avoid duplicating the step-input In `src/wf_cli/commands/draft_options.py`, avoid duplicating the step-input
parser. Extract private helpers parameterized only by audience wording: parser. Extract private helpers parameterized only by audience wording:
@@ -931,7 +931,7 @@ def parse_workflow_output_bindings_file(path: Path) -> list[InputBinding]:
Do not change accepted step-input syntax or its existing error assertions. Do not change accepted step-input syntax or its existing error assertions.
- [ ] **Step 3: Write failing command tests** - [x] **Step 3: Write failing command tests**
Add local CLI tests that use a fake handler with separate canonical and map call Add local CLI tests that use a fake handler with separate canonical and map call
logs. Cover: logs. Cover:
@@ -981,7 +981,7 @@ assert the captured method is
`workflow.draft_workspaces.set_workflow_output_bindings` with the exact ordered `workflow.draft_workspaces.set_workflow_output_bindings` with the exact ordered
mixed binding list. Keep a remote compatibility test for `--merge --map`. mixed binding list. Keep a remote compatibility test for `--merge --map`.
- [ ] **Step 4: Run CLI tests and verify they fail** - [x] **Step 4: Run CLI tests and verify they fail**
Run: Run:
@@ -991,7 +991,7 @@ uv run pytest tests/wf_cli/test_app.py tests/wf_cli/test_remote_target.py -q -k
Expected: parser tests or command tests fail because canonical modes are absent. Expected: parser tests or command tests fail because canonical modes are absent.
- [ ] **Step 5: Replace the command's default path while retaining compatibility** - [x] **Step 5: Replace the command's default path while retaining compatibility**
Update imports and the `set_workflow_output` Typer command in Update imports and the `set_workflow_output` Typer command in
`src/wf_cli/commands/drafts.py`. Parse modes before loading context: `src/wf_cli/commands/drafts.py`. Parse modes before loading context:
@@ -1057,7 +1057,7 @@ Use assertions or explicit branches to satisfy basedpyright narrowing without
`cast(Any, ...)`. Rewrite the docstring/help so `--clear` explicitly says it `cast(Any, ...)`. Rewrite the docstring/help so `--clear` explicitly says it
restores implicit same-name state fallback rather than promising empty output. restores implicit same-name state fallback rather than promising empty output.
- [ ] **Step 6: Run Task 4 verification** - [x] **Step 6: Run Task 4 verification**
Run: Run:
@@ -1068,7 +1068,7 @@ uv run basedpyright --level error
Expected: all pass. Expected: all pass.
- [ ] **Step 7: Review and commit Task 4** - [x] **Step 7: Review and commit Task 4**
Review mode exclusivity, parsing-before-context, flag ordering, local/remote Review mode exclusivity, parsing-before-context, flag ordering, local/remote
parity, and compatibility isolation. Then: parity, and compatibility isolation. Then:
@@ -1092,13 +1092,13 @@ git commit -m "feat: replace workflow output bindings from cli"
- Modify: `skills/wf-workflow/references/draft-workspaces.md` - Modify: `skills/wf-workflow/references/draft-workspaces.md`
- Modify: `skills/wf-workflow/references/workflow-lifecycle.md` - Modify: `skills/wf-workflow/references/workflow-lifecycle.md`
- Modify: `docs/current_roadmap.md` - Modify: `docs/current_roadmap.md`
- Move: `docs/superpowers/plans/2026-07-23-atomic-workflow-output-bindings.md` to `docs/historical/superpowers/plans/2026-07-23-atomic-workflow-output-bindings.md` - Moved: `docs/superpowers/plans/2026-07-23-atomic-workflow-output-bindings.md` to `docs/historical/superpowers/plans/2026-07-23-atomic-workflow-output-bindings.md`
**Interfaces:** **Interfaces:**
- Consumes: verified behavior from Tasks 1-4. - Consumes: verified behavior from Tasks 1-4.
- Produces: current user/agent documentation, accurate issue state, completed roadmap entry, archived checked plan, and final review evidence. - Produces: current user/agent documentation, accurate issue state, completed roadmap entry, archived checked plan, and final review evidence.
- [ ] **Step 1: Update issue state without closing compatibility-map loss** - [x] **Step 1: Update issue state without closing compatibility-map loss**
In `ISSUES.md`: In `ISSUES.md`:
@@ -1109,7 +1109,7 @@ In `ISSUES.md`:
- state that canonical workflow-output replacement preserves path/value union - state that canonical workflow-output replacement preserves path/value union
order while `set_workflow_output_map` remains compatibility-only. order while `set_workflow_output_map` remains compatibility-only.
- [ ] **Step 2: Update live CLI and workflow docs** - [x] **Step 2: Update live CLI and workflow docs**
Update `docs/wf_cli.md` and `docs/workflow_drafts.md` with all canonical modes: Update `docs/wf_cli.md` and `docs/workflow_drafts.md` with all canonical modes:
@@ -1140,7 +1140,7 @@ Update operation inventories in `docs/workflow_capabilities.md` and
`workflow.draft_workspaces.set_workflow_output_bindings` and `workflow.draft_workspaces.set_workflow_output_bindings` and
`wf.workflow.set_workflow_output_bindings`. `wf.workflow.set_workflow_output_bindings`.
- [ ] **Step 3: Update agent instruction surfaces** - [x] **Step 3: Update agent instruction surfaces**
Update `skills/wf-cli/SKILL.md` and the two `wf-workflow` references with the Update `skills/wf-cli/SKILL.md` and the two `wf-workflow` references with the
same canonical examples and decision rule: same canonical examples and decision rule:
@@ -1153,7 +1153,7 @@ round-trip. Use --merge --map only when a lossy compatibility edit is acceptable
Do not direct agents to implementation files or tests. Do not direct agents to implementation files or tests.
- [ ] **Step 4: Update roadmap and archive the checked plan** - [x] **Step 4: Update roadmap and archive the checked plan**
Add a completed milestone to `docs/current_roadmap.md` linking to: Add a completed milestone to `docs/current_roadmap.md` linking to:
@@ -1168,7 +1168,7 @@ Search for the old active-plan path and update any live links:
rg -n "2026-07-23-atomic-workflow-output-bindings" docs skills README.md rg -n "2026-07-23-atomic-workflow-output-bindings" docs skills README.md
``` ```
- [ ] **Step 5: Run the complete focused matrix** - [x] **Step 5: Run the complete focused matrix**
Run: Run:
@@ -1204,7 +1204,7 @@ If the Windows `uv.exe` app alias is broken, use the repository environment:
-q -q
``` ```
- [ ] **Step 6: Run static verification** - [x] **Step 6: Run static verification**
Run: Run:
@@ -1219,7 +1219,7 @@ Expected: all clean. Remove only verified workspace-local `.pytest-*` temporary
directories before Ruff if an interrupted pytest run left generated fixture directories before Ruff if an interrupted pytest run left generated fixture
files under the repository root. files under the repository root.
- [ ] **Step 7: Run independent whole-slice review** - [x] **Step 7: Run independent whole-slice review**
Review from the pre-plan implementation base through `HEAD` against Review from the pre-plan implementation base through `HEAD` against
`docs/superpowers/specs/2026-07-23-atomic-workflow-output-bindings-design.md`. `docs/superpowers/specs/2026-07-23-atomic-workflow-output-bindings-design.md`.
@@ -1239,7 +1239,7 @@ the reviewer to inspect:
Fix all Critical and Important findings, rerun affected tests/static checks, and Fix all Critical and Important findings, rerun affected tests/static checks, and
repeat focused review until both Standards and Spec pass. repeat focused review until both Standards and Spec pass.
- [ ] **Step 8: Commit documentation and any final review fixes** - [x] **Step 8: Commit documentation and any final review fixes**
Commit the documentation/archive change: Commit the documentation/archive change:
+30 -18
View File
@@ -320,7 +320,8 @@ wf draft set-name concat_ws --revision 1 --name concat_ws_v2
wf draft set-route concat_ws --revision 2 --step call --outcome ok --to __end__ wf draft set-route concat_ws --revision 2 --step call --outcome ok --to __end__
wf draft set-input concat_ws --revision 3 --step call --map input.items=items --map input.separator=separator wf draft set-input concat_ws --revision 3 --step call --map input.items=items --map input.separator=separator
wf draft set-output concat_ws --revision 4 --step call --map value=state.value wf draft set-output concat_ws --revision 4 --step call --map value=state.value
wf draft set-workflow-output concat_ws --revision 5 --map state.value=result wf draft set-workflow-output concat_ws --revision 5 \
--map state.value=result --value format='"markdown"'
wf draft set-input concat_ws --revision 6 --step call --merge --map input.limit=limit wf draft set-input concat_ws --revision 6 --step call --merge --map input.limit=limit
wf draft branch concat_ws --revision 7 --step call --route ok=__end__ --route error=tool_error wf draft branch concat_ws --revision 7 --step call --route ok=__end__ --route error=tool_error
wf draft handle concat_ws --revision 8 --to fail --branch lookup:error --branch transform:error wf draft handle concat_ws --revision 8 --to fail --branch lookup:error --branch transform:error
@@ -363,22 +364,33 @@ repeated-source fan-out.
`set-output` maps node-local output fields to workflow state paths: `set-output` maps node-local output fields to workflow state paths:
`text=state.text` means `local.text -> state.text`. `text=state.text` means `local.text -> state.text`.
`set-workflow-output` maps graph source paths (`input.*`, `state.*`, or `set-workflow-output` canonically replaces the complete ordered workflow-output
`context.*`) to top-level output fields: `state.value=result` means binding list. Repeat `--map` for graph source paths (`input.*`, `state.*`, or
`state.value -> output.result`. `context.*`) and `--value` for literal JSON values. For example,
`state.value=result` means `state.value -> output.result`, while
`--value format='"markdown"'` writes a literal to `output.format`.
For single-field `input.*` and `state.*` sources, the command projects missing Nested `input.*` and `state.*` source paths project missing nested
top-level `output_schema` fields from the source schema. More complex or `output_schema` fields from their declared source schemas. Literal bindings and
undeclared paths still rely on `wf draft validate` diagnostics. `context.*` paths do not infer a schema: their output targets must already be
declared, and literals must validate against those declared targets.
By default, `set-input` and `set-output` replace complete ordered canonical Use `--bindings-file` to restore an exported mixed path/value list without
binding lists, while `set-workflow-output` replaces its complete map. Repeated changing its order, or `--clear` to replace the list with `[]`. An empty
`set-output --map LOCAL_SOURCE=STATE_TARGET` flags preserve their order and may workflow-output list restores the runtime's implicit same-name state fallback;
repeat a source to fan it out to several state targets. Use it does not promise an always-empty public output. Use `--merge --map` only for
`set-output --bindings-file` for an exported canonical list and `--clear` for an the compatibility map adapter. It is intentionally lossy and cannot represent
explicit empty replacement. Use `--merge --map` only for compatibility output literals, canonical ordering, or repeated-source fan-out reliably.
map edits; that path cannot preserve canonical ordering or repeated-source
fan-out. ```bash
wf draft inspect WS --include-draft |
jq '.draft.output' > output-bindings.json
wf draft set-workflow-output WS --revision 5 \
--bindings-file output-bindings.json
wf draft set-workflow-output WS --revision 6 --clear
```
### Bind A Step Path ### Bind A Step Path
@@ -413,10 +425,10 @@ Repair-hint examples:
```bash ```bash
# Declare an undeclared workflow input field and bind it to a step input # Declare an undeclared workflow input field and bind it to a step input
wf draft bind report_ws --revision 4 --step read --from input.path --to local.path wf draft bind report_ws --revision 4 --step read --from input.path --to local.path
# Request a public workflow output; bind lowers it through state internally # Request a public workflow output through an explicit state projection
wf draft bind report_ws --revision 5 --step render --from local.markdown --to output.markdown wf draft bind report_ws --revision 5 --step render --from local.markdown --to output.markdown
# Set workflow output independently (no schema projection) # Set workflow output independently, including a nested source projection
wf draft set-workflow-output report_ws --revision 6 --map state.markdown=markdown wf draft set-workflow-output report_ws --revision 6 --map state.report.markdown=markdown
``` ```
The command combines two common edits: The command combines two common edits:
+12 -1
View File
@@ -155,10 +155,21 @@ Focused repair helpers:
- `wf.workflow.set_step_output_bindings` - `wf.workflow.set_step_output_bindings`
- `wf.workflow.set_step_input_map` - `wf.workflow.set_step_input_map`
- `wf.workflow.set_step_output_map` - `wf.workflow.set_step_output_map`
- `wf.workflow.set_workflow_output_bindings`
- `wf.workflow.set_workflow_output_map` (compatibility-only map adapter)
These helpers are deliberately narrow. Prefer them over JSON Patch when the These helpers are deliberately narrow. Prefer them over JSON Patch when the
caller only needs to edit one common field. caller only needs to edit one common field.
`wf.workflow.set_workflow_output_bindings` replaces the complete ordered
top-level output projection. Its `bindings` list can mix graph paths and
literal values. Nested `input.*` and `state.*` paths may project missing
output-schema fields from declared source schemas; literal values and
`context.*` paths require declared output targets. An empty list restores the
implicit same-name state fallback. Use the map tool only for compatibility:
`set_workflow_output_map` cannot preserve canonical path/value order, literals,
or repeated-source fan-out.
Advanced workspace tools: Advanced workspace tools:
- `wf.workflow.list_draft_workspaces`: find mutable draft sessions. - `wf.workflow.list_draft_workspaces`: find mutable draft sessions.
@@ -545,7 +556,7 @@ resending the full draft each turn.
| Fetch current draft workspace | `wf.workflow.get_draft_workspace` | | Fetch current draft workspace | `wf.workflow.get_draft_workspace` |
| Patch current draft workspace | `wf.workflow.patch_draft_workspace` | | Patch current draft workspace | `wf.workflow.patch_draft_workspace` |
| Refresh validation without changing revision | `wf.workflow.validate_draft_workspace` | | Refresh validation without changing revision | `wf.workflow.validate_draft_workspace` |
| Change common draft fields without JSON Patch | `wf.workflow.set_draft_name`, `wf.workflow.set_draft_route`, `wf.workflow.set_step_input_bindings`, `wf.workflow.set_step_output_bindings`, `wf.workflow.set_step_input_map`, `wf.workflow.set_step_output_map` | | Change common draft fields without JSON Patch | `wf.workflow.set_draft_name`, `wf.workflow.set_draft_route`, `wf.workflow.set_step_input_bindings`, `wf.workflow.set_step_output_bindings`, `wf.workflow.set_step_input_map`, `wf.workflow.set_step_output_map`, `wf.workflow.set_workflow_output_bindings` |
| Save final workspace as artifact | `wf.workflow.create_artifact_from_workspace` | | Save final workspace as artifact | `wf.workflow.create_artifact_from_workspace` |
| Save final workspace as callable wrapper | `wf.workflow.create_wrapper_from_workspace` | | Save final workspace as callable wrapper | `wf.workflow.create_wrapper_from_workspace` |
| Clean up a draft workspace | `wf.workflow.delete_draft_workspace` | | Clean up a draft workspace | `wf.workflow.delete_draft_workspace` |
+2 -1
View File
@@ -313,7 +313,8 @@ For the copy-less wrapper authoring path, use:
2. `wf.workflow.create_draft_workspace_from_capability` to create a patchable 2. `wf.workflow.create_draft_workspace_from_capability` to create a patchable
draft workspace from those hints. draft workspace from those hints.
3. Focused patch helpers such as `wf.workflow.set_step_input_bindings`, 3. Focused patch helpers such as `wf.workflow.set_step_input_bindings`,
`wf.workflow.set_step_output_bindings`, their compatibility map adapters, `wf.workflow.set_step_output_bindings`,
`wf.workflow.set_workflow_output_bindings`, their compatibility map adapters,
and `wf.workflow.set_draft_route` to fix low-confidence hints or explicit and `wf.workflow.set_draft_route` to fix low-confidence hints or explicit
`missing_decisions`. `missing_decisions`.
4. `wf.workflow.validate_draft_workspace` to refresh diagnostics. 4. `wf.workflow.validate_draft_workspace` to refresh diagnostics.
+27 -3
View File
@@ -151,7 +151,16 @@ state.result_text -> workflow output.result_text
If top-level `output` is empty, the runtime keeps the legacy same-name fallback: If top-level `output` is empty, the runtime keeps the legacy same-name fallback:
for every field in `output_schema`, it copies the top-level state field with the for every field in `output_schema`, it copies the top-level state field with the
same name when present. That fallback is convenient, but explicit output same name when present. That fallback is convenient, but explicit output
projection is clearer for new workflows. projection is clearer for new workflows. Clearing canonical output bindings
restores this fallback; it does not mean that the public output is always empty.
Canonical workflow-output replacement accepts an ordered union of path and
literal bindings. Nested `input.*` and `state.*` sources can project missing
nested output-schema fields from their declared source schemas. Literal values
and `context.*` paths require an already-declared output target; literals are
validated against that target and never used to infer a schema. Equal or
ancestor/descendant output targets are rejected so the ordered list has one
unambiguous public projection.
## Explicit Outputs And Error Outcomes ## Explicit Outputs And Error Outcomes
@@ -711,8 +720,13 @@ wf draft set-output <workspace_id> --revision <n> --step <step_id> --map text=st
wf draft set-output <workspace_id> --revision <n> --step <step_id> --bindings-file output-bindings.json wf draft set-output <workspace_id> --revision <n> --step <step_id> --bindings-file output-bindings.json
wf draft set-output <workspace_id> --revision <n> --step <step_id> --clear wf draft set-output <workspace_id> --revision <n> --step <step_id> --clear
wf draft set-output <workspace_id> --revision <n> --step <step_id> --merge --map other=state.other wf draft set-output <workspace_id> --revision <n> --step <step_id> --merge --map other=state.other
wf draft set-workflow-output <workspace_id> --revision <n> --map state.value=result wf draft set-workflow-output <workspace_id> --revision <n> \
wf draft set-workflow-output <workspace_id> --revision <n> --merge --map state.other=other --map state.value=result --value format='"markdown"'
wf draft set-workflow-output <workspace_id> --revision <n> \
--bindings-file output-bindings.json
wf draft set-workflow-output <workspace_id> --revision <n> --clear
wf draft set-workflow-output <workspace_id> --revision <n> \
--merge --map state.other=other
wf draft branch <workspace_id> --revision <n> --step <step_id> --route ok=__end__ --route error=tool_error wf draft branch <workspace_id> --revision <n> --step <step_id> --route ok=__end__ --route error=tool_error
wf draft handle <workspace_id> --revision <n> --to fail --branch lookup:error --branch transform:error wf draft handle <workspace_id> --revision <n> --to fail --branch lookup:error --branch transform:error
wf draft compile <workspace_id> wf draft compile <workspace_id>
@@ -726,6 +740,16 @@ empty replacement. The legacy `--merge --map` path is compatibility-only and
potentially lossy because a map cannot preserve ordering or repeated-source potentially lossy because a map cannot preserve ordering or repeated-source
fan-out. fan-out.
`set-workflow-output` follows the same canonical replacement model for the
top-level `WorkflowDraft.output` list. Use repeated `--map` and `--value`
flags for a small ordered edit, or `--bindings-file` for an exact round trip
from `draft inspect --include-draft`. `--clear` writes an empty list and
restores the implicit same-name state fallback. Literal and `context.*`
bindings need declared output targets; nested `input.*` and `state.*` paths
may project their declared source schema into missing nested output fields.
The compatibility `--merge --map` form remains available only for lossy map
edits and cannot preserve literals, order, or repeated-source fan-out.
Use `draft patch` when these focused commands do not cover the structural edit. Use `draft patch` when these focused commands do not cover the structural edit.
Drafts are not raw workflow plans. Drafts use `steps`, `routes`, and step field Drafts are not raw workflow plans. Drafts use `steps`, `routes`, and step field
+18 -6
View File
@@ -51,8 +51,13 @@ wf draft set-output <workspace_id> --revision <n> --step <step_id> --map text=st
wf draft set-output <workspace_id> --revision <n> --step <step_id> --bindings-file bindings.json wf draft set-output <workspace_id> --revision <n> --step <step_id> --bindings-file bindings.json
wf draft set-output <workspace_id> --revision <n> --step <step_id> --clear wf draft set-output <workspace_id> --revision <n> --step <step_id> --clear
wf draft set-output <workspace_id> --revision <n> --step <step_id> --merge --map other=state.other wf draft set-output <workspace_id> --revision <n> --step <step_id> --merge --map other=state.other
wf draft set-workflow-output <workspace_id> --revision <n> --map state.value=result wf draft set-workflow-output <workspace_id> --revision <n> \
wf draft set-workflow-output <workspace_id> --revision <n> --merge --map state.other=other --map state.value=result --value format='"markdown"'
wf draft set-workflow-output <workspace_id> --revision <n> \
--bindings-file output-bindings.json
wf draft set-workflow-output <workspace_id> --revision <n> --clear
wf draft set-workflow-output <workspace_id> --revision <n> \
--merge --map state.other=other
wf draft branch <workspace_id> --revision <n> --step <step_id> --route ok=__end__ --route error=fail wf draft branch <workspace_id> --revision <n> --step <step_id> --route ok=__end__ --route error=fail
wf draft handle <workspace_id> --revision <n> --to fail --branch lookup:error --branch transform:error wf draft handle <workspace_id> --revision <n> --to fail --branch lookup:error --branch transform:error
wf draft compile <workspace_id> wf draft compile <workspace_id>
@@ -90,10 +95,12 @@ compatibility map-only edit when the workflow schema is already declared.
local side, so their targets are rootless paths: write local side, so their targets are rootless paths: write
`input.title=report.title`, not `input.title=local.report.title`. `input.title=report.title`, not `input.title=local.report.title`.
`wf draft set-workflow-output` projects missing public output schema fields for `wf draft set-workflow-output` replaces the complete ordered public output
single-field `input.*` and `state.*` sources. Prefer it for final workflow projection. Nested `input.*` and `state.*` sources can project missing nested
outputs; use `wf draft bind --from local.x --to output.y` when the source is a output-schema fields from declared source schemas. Literal values and
step-local capability output. `context.*` paths require declared output targets; literals validate against
those targets and do not infer schemas. Use `wf draft bind --from local.x --to
output.y` when the source is a step-local capability output.
When `wf draft validate` returns a `repair_hint`, run that exact focused command When `wf draft validate` returns a `repair_hint`, run that exact focused command
before writing JSON Patch manually. To make one capability output public, use before writing JSON Patch manually. To make one capability output public, use
@@ -171,6 +178,11 @@ wf draft set-output WS --revision 6 --step analyze --clear
`--merge --map` is compatibility-only and may collapse existing fan-out. Use `--merge --map` is compatibility-only and may collapse existing fan-out. Use
it only when a lossy map edit is acceptable. it only when a lossy map edit is acceptable.
Use `set-workflow-output` without `--merge` when replacing the complete public
output projection. Use `--bindings-file` when exact path/value interleaving
must round-trip, and `--clear` to restore implicit same-name state fallback.
Use `--merge --map` only when a lossy compatibility edit is acceptable.
Prefer `draft bind` when a capability step binding also needs schema Prefer `draft bind` when a capability step binding also needs schema
projection. Use `input/state -> local` for step inputs and `local -> projection. Use `input/state -> local` for step inputs and `local ->
state/output` for step outputs. It requires a capability-backed step with state/output` for step outputs. It requires a capability-backed step with
@@ -87,7 +87,8 @@ Prefer focused helpers over JSON Patch for common edits:
- `set_step_input_map` - `set_step_input_map`
- `set_step_output_bindings` - `set_step_output_bindings`
- `set_step_output_map` - `set_step_output_map`
- `set_workflow_output_map` - `set_workflow_output_bindings`
- `set_workflow_output_map` (compatibility-only map adapter)
- `bind_draft` - `bind_draft`
- `add_step` - `add_step`
- `add_step_from_capability` - `add_step_from_capability`
@@ -109,8 +110,13 @@ wf draft set-output <workspace_id> --revision <n> --step <step_id> --map text=st
wf draft set-output <workspace_id> --revision <n> --step <step_id> --bindings-file bindings.json wf draft set-output <workspace_id> --revision <n> --step <step_id> --bindings-file bindings.json
wf draft set-output <workspace_id> --revision <n> --step <step_id> --clear wf draft set-output <workspace_id> --revision <n> --step <step_id> --clear
wf draft set-output <workspace_id> --revision <n> --step <step_id> --merge --map other=state.other wf draft set-output <workspace_id> --revision <n> --step <step_id> --merge --map other=state.other
wf draft set-workflow-output <workspace_id> --revision <n> --map state.value=result wf draft set-workflow-output <workspace_id> --revision <n> \
wf draft set-workflow-output <workspace_id> --revision <n> --merge --map state.other=other --map state.value=result --value format='"markdown"'
wf draft set-workflow-output <workspace_id> --revision <n> \
--bindings-file output-bindings.json
wf draft set-workflow-output <workspace_id> --revision <n> --clear
wf draft set-workflow-output <workspace_id> --revision <n> \
--merge --map state.other=other
wf draft branch <workspace_id> --revision <n> --step <step_id> --route ok=__end__ --route error=fail wf draft branch <workspace_id> --revision <n> --step <step_id> --route ok=__end__ --route error=fail
wf draft handle <workspace_id> --revision <n> --to fail --branch lookup:error --branch transform:error wf draft handle <workspace_id> --revision <n> --to fail --branch lookup:error --branch transform:error
wf draft compile <workspace_id> wf draft compile <workspace_id>
@@ -121,11 +127,13 @@ wf draft add interrupt <workspace_id> --revision <n> --step review --kind issue_
wf draft add when <workspace_id> --revision <n> --step decide --condition-file condition.json --then next --otherwise revise wf draft add when <workspace_id> --revision <n> --step decide --condition-file condition.json --then next --otherwise revise
``` ```
`set-workflow-output` maps a graph source path (`input.*`, `state.*`, or `set-workflow-output` replaces the complete ordered top-level
`context.*`) to one public workflow output field. It edits top-level `WorkflowDraft.output` binding list. It accepts graph source paths
`WorkflowDraft.output`; `set-output` edits one step's local-to-state bindings. (`input.*`, `state.*`, or `context.*`) and literal values; `set-output` edits
For single-field `input.*` and `state.*` sources, missing public output schema one step's local-to-state bindings. Nested `input.*` and `state.*` sources can
fields are projected automatically from the source schema. project missing output-schema fields from their declared source schemas.
Literal values and `context.*` paths require declared output targets, and
literal values are validated against those targets rather than inferred.
`set-input` direction: `input.title=report.title` means graph source `set-input` direction: `input.title=report.title` means graph source
`input.title` maps to node-local target `local.report.title`. Targets are `input.title` maps to node-local target `local.report.title`. Targets are
@@ -178,9 +186,10 @@ wf draft set-output WS --revision 5 --step analyze \
wf draft set-output WS --revision 6 --step analyze --clear wf draft set-output WS --revision 6 --step analyze --clear
``` ```
`--merge --map` is compatibility-only and may collapse existing fan-out. Use `--clear` replaces the list with `[]` and restores the implicit same-name state
it only when a lossy map edit is acceptable. `set-workflow-output` retains its fallback. `--merge --map` is compatibility-only and may collapse existing
map replacement behavior and is a separate workflow-boundary operation. fan-out; it cannot preserve literals or canonical ordering. Use it only when a
lossy map edit is acceptable.
`bind input.title -> local.report.title` is schema-aware and idempotent when `bind input.title -> local.report.title` is schema-aware and idempotent when
`input.title` is already declared. Bind names both rooted endpoints explicitly. `input.title` is already declared. Bind names both rooted endpoints explicitly.
@@ -16,10 +16,15 @@ validated, runnable deployment.
5. Inspect/patch/validate the workspace until valid. 5. Inspect/patch/validate the workspace until valid.
- Use focused CLI commands (`set-name`, `set-route`, `set-input`, `set-output`) - Use focused CLI commands (`set-name`, `set-route`, `set-input`, `set-output`)
for common edits. for common edits.
- `set-input` and `set-output` replace their ordered canonical binding lists. - `set-input`, `set-output`, and `set-workflow-output` replace their ordered
canonical binding lists.
Repeated sources preserve fan-out to distinct targets. The `--merge` Repeated sources preserve fan-out to distinct targets. The `--merge`
variants are compatibility-only and cannot preserve canonical ordering or variants are compatibility-only and cannot preserve canonical ordering or
repeated-source fan-out; existing literals are retained for input merges. repeated-source fan-out; existing literals are retained for input merges.
- `set-workflow-output` accepts ordered path/value bindings. Nested
`input.*` and `state.*` sources can project declared source schemas into
missing output fields; literals and `context.*` require declared targets.
`--clear` restores implicit same-name state fallback.
- Before mapping into a new workflow input, state, or output field, prefer - Before mapping into a new workflow input, state, or output field, prefer
`wf draft bind --from ... --to ...` when it should mirror a capability `wf draft bind --from ... --to ...` when it should mirror a capability
local input/output property. It declares the matching schema and merges local input/output property. It declares the matching schema and merges