feat: simplify draft cli vocabulary

This commit is contained in:
lda
2026-06-28 01:08:42 +07:00 Verified
parent 955c43d808
commit 74b66c9db7
17 changed files with 687 additions and 43 deletions
+4 -1
View File
@@ -67,7 +67,10 @@ clear operator feedback before adding more architecture.
schema projection with step binding merge, replacing the narrower schema projection with step binding merge, replacing the narrower
`bind-output-to-state` helper and reducing manual draft patch repairs in `bind-output-to-state` helper and reducing manual draft patch repairs in
agent challenge runs. agent challenge runs.
- Completed: `wf draft add-step-from-capability` inserts one explicit - Completed: draft CLI vocabulary now uses `wf draft create --capability` and
`wf draft add-step --capability`, replacing the longer
`*-from-capability` commands that agents repeatedly guessed around.
- Completed: `wf draft add-step` inserts one explicit
capability-backed step with route, input, and output-to-state schema/binding capability-backed step with route, input, and output-to-state schema/binding
wiring in a single revision, reducing brittle JSON Patch authoring for wiring in a single revision, reducing brittle JSON Patch authoring for
multi-step workflows. Accepts `--route OUTCOME=TARGET` for multi-outcome steps. multi-step workflows. Accepts `--route OUTCOME=TARGET` for multi-outcome steps.
@@ -0,0 +1,496 @@
# Draft CLI Vocabulary 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:** Replace long draft CLI command names with `wf draft create --capability` and `wf draft add-step --capability`.
**Architecture:** This is a CLI vocabulary cleanup, not a programmatic API rename. Keep the existing Python/RPC/MCP methods and route the new Typer commands to those handlers. Remove the old long CLI commands and update live docs/skills so agents learn one command shape.
**Tech Stack:** Python 3.14, Typer CLI, pytest, ruff, basedpyright.
---
## File Map
- Modify `src/wf_cli/commands/drafts.py`: replace `create-from-capability` with `create`, replace `add-step-from-capability` with `add-step`.
- Modify `tests/wf_cli/test_app.py`: update help tests and assert old long commands are absent.
- Modify `tests/wf_cli/test_remote_target.py` and `tests/wf_cli/test_discovery_lifecycle.py`: update CLI invocations.
- Modify live docs/skills that mention the old CLI commands:
- `docs/wf_cli.md`
- `docs/current_roadmap.md`
- `docs/runbooks/rpc-cli-smoke.md`
- `docs/runbooks/python-source.md`
- `skills/wf-cli/SKILL.md`
- `skills/wf-workflow/SKILL.md`
- `skills/wf-workflow/references/workflow-lifecycle.md`
- `skills/wf-workflow/references/draft-workspaces.md`
- `skills/wf-workflow/references/direct-plan-import.md`
- `skills/wf-workflow/references/system-model.md`
- `docs/superpowers/specs/2026-06-27-draft-semantic-authoring-boundary.md`
- Do not edit historical docs except by moving this plan after implementation.
---
### Task 1: Replace Create Command Shape
**Files:**
- Modify: `src/wf_cli/commands/drafts.py`
- Test: `tests/wf_cli/test_app.py`, `tests/wf_cli/test_remote_target.py`, `tests/wf_cli/test_discovery_lifecycle.py`
- [ ] **Step 1: Update failing CLI help tests**
In `tests/wf_cli/test_app.py`, replace:
```python
def test_wf_draft_create_from_capability_help_exists() -> None:
result = runner.invoke(app, ["draft", "create-from-capability", "--help"])
assert result.exit_code == 0
assert "--title" in result.output
```
with:
```python
def test_wf_draft_create_help_accepts_capability_option() -> None:
result = runner.invoke(app, ["draft", "create", "--help"])
assert result.exit_code == 0
output = " ".join(result.output.split())
assert "--capability" in output
assert "--title" in output
def test_wf_draft_help_does_not_list_old_create_from_capability() -> None:
result = runner.invoke(app, ["draft", "--help"])
assert result.exit_code == 0
assert "create-from-capability" not in result.output
```
- [ ] **Step 2: Update CLI invocations in tests**
Replace command arrays like:
```python
[
"draft",
"create-from-capability",
"workspace_id",
"wf.std.constant",
]
```
with:
```python
[
"draft",
"create",
"workspace_id",
"--capability",
"wf.std.constant",
]
```
Files to update:
- `tests/wf_cli/test_remote_target.py`
- `tests/wf_cli/test_discovery_lifecycle.py`
Use `rg -n 'create-from-capability' tests/wf_cli` to find every live test hit.
- [ ] **Step 3: Run tests and confirm failure**
Run:
```powershell
uv run pytest tests/wf_cli/test_app.py tests/wf_cli/test_remote_target.py tests/wf_cli/test_discovery_lifecycle.py -q -k "draft or create"
```
Expected: failures because `wf draft create` does not exist yet.
- [ ] **Step 4: Implement `wf draft create --capability`**
In `src/wf_cli/commands/drafts.py`, replace:
```python
@app.command("create-from-capability")
def create_from_capability(
ctx: typer.Context,
workspace_id: Annotated[str, typer.Argument(help="Draft workspace id.")],
capability_name: Annotated[str, typer.Argument(help="Workflow capability name.")],
name: Annotated[
str | None, typer.Option("--name", help="Draft workflow name.")
] = None,
title: Annotated[
str | None, typer.Option("--title", help="Workspace title.")
] = None,
) -> None:
"""Bootstrap a draft workspace from inspect_capability wrapper hints."""
```
with:
```python
@app.command("create")
def create_from_capability(
ctx: typer.Context,
workspace_id: Annotated[str, typer.Argument(help="Draft workspace id.")],
capability_name: Annotated[
str,
typer.Option(
"--capability",
help="Qualified capability name used to bootstrap the draft.",
),
],
name: Annotated[
str | None, typer.Option("--name", help="Draft workflow name.")
] = None,
title: Annotated[
str | None, typer.Option("--title", help="Workspace title.")
] = None,
) -> None:
"""Create a patchable draft workspace from one capability."""
```
Keep the handler call unchanged:
```python
context.handlers.create_draft_workspace_from_capability(...)
```
Do not keep `@app.command("create-from-capability")`.
- [ ] **Step 5: Run create-focused tests**
Run:
```powershell
uv run pytest tests/wf_cli/test_app.py tests/wf_cli/test_remote_target.py tests/wf_cli/test_discovery_lifecycle.py -q -k "create"
```
Expected: selected tests pass.
- [ ] **Step 6: Commit**
```powershell
git add src/wf_cli/commands/drafts.py tests/wf_cli/test_app.py tests/wf_cli/test_remote_target.py tests/wf_cli/test_discovery_lifecycle.py
git commit -m "feat: simplify draft create cli"
```
---
### Task 2: Replace Add-Step Command Shape
**Files:**
- Modify: `src/wf_cli/commands/drafts.py`
- Test: `tests/wf_cli/test_app.py`, `tests/wf_cli/test_remote_target.py`
- [ ] **Step 1: Update failing CLI help tests**
In `tests/wf_cli/test_app.py`, replace:
```python
def test_wf_draft_add_step_from_capability_help_explains_explicit_wiring() -> None:
result = runner.invoke(app, ["draft", "add-step-from-capability", "--help"])
```
with:
```python
def test_wf_draft_add_step_help_explains_explicit_wiring() -> None:
result = runner.invoke(app, ["draft", "add-step", "--help"])
assert result.exit_code == 0
output = " ".join(result.output.split())
assert "--capability" in output
assert "--from-step" in output
assert "--bind-output" in output
assert "does not guess" in output
def test_wf_draft_help_does_not_list_old_add_step_from_capability() -> None:
result = runner.invoke(app, ["draft", "--help"])
assert result.exit_code == 0
assert "add-step-from-capability" not in result.output
```
- [ ] **Step 2: Update CLI invocations in tests**
Replace:
```python
[
"draft",
"add-step-from-capability",
"workspace_id",
"--revision",
"1",
"--step",
"render",
"--capability",
"local.report.render_markdown_report",
]
```
with:
```python
[
"draft",
"add-step",
"workspace_id",
"--revision",
"1",
"--step",
"render",
"--capability",
"local.report.render_markdown_report",
]
```
Files to update:
- `tests/wf_cli/test_app.py`
- `tests/wf_cli/test_remote_target.py`
Use `rg -n 'add-step-from-capability' tests/wf_cli` to find every live test hit.
- [ ] **Step 3: Run tests and confirm failure**
Run:
```powershell
uv run pytest tests/wf_cli/test_app.py tests/wf_cli/test_remote_target.py -q -k "add_step or add-step"
```
Expected: failures because `wf draft add-step` does not exist yet.
- [ ] **Step 4: Implement `wf draft add-step --capability`**
In `src/wf_cli/commands/drafts.py`, replace:
```python
@app.command("add-step-from-capability")
def add_step_from_capability(
```
with:
```python
@app.command("add-step")
def add_step_from_capability(
```
Update the docstring first line to:
```python
"""Add one capability-backed step with explicit route, input, and output wiring.
```
Keep the handler call unchanged:
```python
context.handlers.add_step_from_capability(...)
```
Do not keep `@app.command("add-step-from-capability")`.
- [ ] **Step 5: Run add-step tests**
Run:
```powershell
uv run pytest tests/wf_cli/test_app.py tests/wf_cli/test_remote_target.py -q -k "add_step or add-step"
```
Expected: selected tests pass.
- [ ] **Step 6: Commit**
```powershell
git add src/wf_cli/commands/drafts.py tests/wf_cli/test_app.py tests/wf_cli/test_remote_target.py
git commit -m "feat: simplify draft add-step cli"
```
---
### Task 3: Update Live Docs And Skills
**Files:**
- Modify: `docs/wf_cli.md`
- Modify: `docs/current_roadmap.md`
- Modify: `docs/runbooks/rpc-cli-smoke.md`
- Modify: `docs/runbooks/python-source.md`
- Modify: `docs/superpowers/specs/2026-06-27-draft-semantic-authoring-boundary.md`
- Modify: `skills/wf-cli/SKILL.md`
- Modify: `skills/wf-workflow/SKILL.md`
- Modify: `skills/wf-workflow/references/workflow-lifecycle.md`
- Modify: `skills/wf-workflow/references/draft-workspaces.md`
- Modify: `skills/wf-workflow/references/direct-plan-import.md`
- Modify: `skills/wf-workflow/references/system-model.md`
- Move: `docs/superpowers/plans/2026-06-28-draft-cli-vocabulary.md` to `docs/historical/superpowers/plans/2026-06-28-draft-cli-vocabulary.md`
- [ ] **Step 1: Replace old command names in live docs/skills**
Use:
```powershell
rg -n 'create-from-capability|add-step-from-capability|draft create --capability' docs skills -S -g '!docs/historical/**'
```
Update live CLI examples:
```powershell
wf draft create report_ws --capability local.report.extract_report --name report
```
```powershell
wf draft add-step report_ws --revision 3 --step render --capability local.report.render_markdown_report --route ok=__end__
```
Remove text that says:
```text
There is currently no `wf draft create --capability` alias.
```
- [ ] **Step 2: Update semantic authoring spec**
In `docs/superpowers/specs/2026-06-27-draft-semantic-authoring-boundary.md`,
replace public CLI operation names:
```markdown
- `create`
- `add-step`
- `bind`
- `branch`
- `handle`
```
Keep Python method names when the document explicitly discusses internal API
implementation details.
- [ ] **Step 3: Update roadmap**
Add a completed bullet under Priority 1:
```markdown
- Completed: draft CLI vocabulary now uses `wf draft create --capability` and
`wf draft add-step --capability`, replacing the longer
`*-from-capability` commands that agents repeatedly guessed around.
```
- [ ] **Step 4: Move plan to historical**
Run:
```powershell
New-Item -ItemType Directory -Force -Path docs/historical/superpowers/plans | Out-Null
Move-Item docs/superpowers/plans/2026-06-28-draft-cli-vocabulary.md docs/historical/superpowers/plans/2026-06-28-draft-cli-vocabulary.md
```
- [ ] **Step 5: Verify no stale live CLI names remain**
Run:
```powershell
rg -n 'create-from-capability|add-step-from-capability' docs skills tests src -S -g '!docs/historical/**'
```
Expected:
- No CLI command docs or tests use the old names.
- Programmatic API/RPC/MCP names may still appear, for example
`create_draft_workspace_from_capability`, `add_step_from_capability`, and
`workflow.draft_workspaces.add_step_from_capability`.
- [ ] **Step 6: Commit**
```powershell
git add docs skills tests src docs/historical/superpowers/plans/2026-06-28-draft-cli-vocabulary.md
git commit -m "docs: update draft cli vocabulary"
```
---
### Task 4: Final Verification
**Files:**
- No planned source edits.
- [ ] **Step 1: Run focused CLI tests**
Run:
```powershell
uv run pytest tests/wf_cli/test_app.py tests/wf_cli/test_remote_target.py tests/wf_cli/test_discovery_lifecycle.py -q -k "draft or create or add_step or add-step"
```
Expected: all selected tests pass.
- [ ] **Step 2: Run broader affected suites**
Run:
```powershell
uv run pytest tests/wf_cli tests/docs -q
```
Expected: all tests pass. If docs tests are not present in the current checkout,
record that and continue with the CLI tests.
- [ ] **Step 3: Run lint, format, and type checks**
Run:
```powershell
uv run ruff check
uv run ruff format --check
uv run basedpyright --level error
git diff --check
```
Expected:
- Ruff clean.
- Format clean.
- Basedpyright reports `0 errors`.
- `git diff --check` has no whitespace errors. CRLF warnings are acceptable on
Windows.
- [ ] **Step 4: Optional smoke command**
Run:
```powershell
uv run wf --config wf.config.json draft create --help
uv run wf --config wf.config.json draft add-step --help
```
Expected:
- `create` help shows `--capability`.
- `add-step` help shows `--capability`, `--route`, `--input`, and
`--bind-output`.
- [ ] **Step 5: Commit final cleanup if needed**
```powershell
git status --short
git add <only files changed by cleanup>
git commit -m "fix: polish draft cli vocabulary"
```
Skip this commit if the tree is already clean after Task 3.
---
## Self-Review Notes
- This plan intentionally changes only CLI command names and live docs/skills.
- It does not rename Python/RPC/MCP programmatic method names.
- It removes the old long CLI names rather than keeping aliases.
- Future work can rename RPC/MCP tools if evidence shows model confusion there too.
+2 -2
View File
@@ -137,8 +137,8 @@ Expected output includes:
Create a draft from the Python capability: Create a draft from the Python capability:
```powershell ```powershell
uv run wf --url http://127.0.0.1:8766/rpc draft create-from-capability ` uv run wf --url http://127.0.0.1:8766/rpc draft create `
python_echo_ws local.ops.echo --name python_echo python_echo_ws --capability local.ops.echo --name python_echo
``` ```
Save it as an artifact. Bind the configured Python source. Built-in platform Save it as an artifact. Bind the configured Python source. Built-in platform
+1 -1
View File
@@ -109,7 +109,7 @@ workflow output.
Use unique ids so repeated runs do not collide: Use unique ids so repeated runs do not collide:
```bash ```bash
uv run wf --config wf.config.json draft create-from-capability smoke_ws wf.std.constant --name smoke_constant --title "Smoke Constant" uv run wf --config wf.config.json draft create smoke_ws --capability wf.std.constant --name smoke_constant --title "Smoke Constant"
uv run wf --config wf.config.json draft validate smoke_ws uv run wf --config wf.config.json draft validate smoke_ws
uv run wf --config wf.config.json draft save smoke_ws --artifact smoke_artifact --version 1 --title "Smoke Artifact" --outcome ok uv run wf --config wf.config.json draft save smoke_ws --artifact smoke_artifact --version 1 --title "Smoke Artifact" --outcome ok
uv run wf --config wf.config.json deploy save smoke_deploy --artifact smoke_artifact --version 1 uv run wf --config wf.config.json deploy save smoke_deploy --artifact smoke_artifact --version 1
@@ -107,8 +107,8 @@ The public draft surface is documented in descending order of preference.
### Semantic Authoring Operations ### Semantic Authoring Operations
- `create-from-capability` - `create`
- `add-step-from-capability` - `add-step`
- `bind` - `bind`
- `branch` - `branch`
- `handle` - `handle`
@@ -149,7 +149,7 @@ authoring intent.
### Add Step From Capability ### Add Step From Capability
`add-step-from-capability` atomically adds: `add-step` atomically adds:
- one capability-backed `use` step; - one capability-backed `use` step;
- explicit input bindings; - explicit input bindings;
@@ -160,7 +160,7 @@ authoring intent.
The outgoing CLI option is repeatable: The outgoing CLI option is repeatable:
```powershell ```powershell
wf draft add-step-from-capability WORKSPACE ` wf draft add-step WORKSPACE `
--revision 4 ` --revision 4 `
--step second_echo ` --step second_echo `
--capability everything.default.echo ` --capability everything.default.echo `
@@ -0,0 +1,116 @@
# Draft CLI Vocabulary Design
## Status
Planned. This design generalizes the two remaining capability-specific draft
CLI verbs:
- `wf draft create-from-capability`
- `wf draft add-step-from-capability`
They are replaced at the CLI layer by:
- `wf draft create --capability <qualified_name>`
- `wf draft add-step --capability <qualified_name>`
The long commands should be removed from the CLI, docs, and skills rather than
kept as aliases. The programmatic API/RPC/MCP method names are not renamed in
this slice because they are older, tested surfaces with broader callers than
the CLI benchmark loop.
## Problem
Agents keep trying CLI shapes that match common command vocabulary:
```powershell
wf draft create --capability local.report.extract_report ...
wf draft add-step --capability local.report.render_markdown_report ...
```
The product currently exposes:
```powershell
wf draft create-from-capability ...
wf draft add-step-from-capability ...
```
The long names are precise but hostile to discovery. Skills currently have to
warn agents that `wf draft create --capability` does not exist, which is a sign
that the CLI shape is wrong.
## New CLI Shape
Create a workspace:
```powershell
wf draft create <workspace_id> `
--capability <qualified_name> `
--name <draft_name> `
--title <workspace_title>
```
Add a capability step:
```powershell
wf draft add-step <workspace_id> `
--revision <n> `
--step <step_id> `
--capability <qualified_name> `
--from-step <prev_step> `
--from-outcome ok `
--route ok=__end__ `
--input input.text=local.text `
--bind-output result=state.result
```
`--capability` is required for both commands in this slice. Future core step
types can extend `add-step` with `--type condition`, `--type foreach`, or
similar flags, but this slice only renames the current capability-backed
operations.
## Removal Policy
Remove these CLI commands:
- `wf draft create-from-capability`
- `wf draft add-step-from-capability`
Do not keep aliases. These names made sense as implementation descriptions, but
they now actively fight agent behavior. Removing them keeps `wf draft --help`
smaller and prevents skills from teaching two ways to do the same operation.
Keep these programmatic method names for now:
- `create_draft_workspace_from_capability`
- `add_step_from_capability`
- `workflow.draft_workspaces.create_from_capability`
- `workflow.draft_workspaces.add_step_from_capability`
- `wf.workflow.create_draft_workspace_from_capability`
- `wf.workflow.add_step_from_capability`
Those surfaces are outside the immediate CLI vocabulary problem and have older
test/docs coverage. A future transport vocabulary cleanup can rename them if
there is evidence that MCP/RPC callers struggle with the same names.
## Documentation Policy
Live user-facing docs and skills should teach only:
```powershell
wf draft create ... --capability ...
wf draft add-step ... --capability ...
```
Historical docs may keep old command names. Live docs that mention the old CLI
names must either be updated or clearly mark them as historical context.
## Acceptance Criteria
- `wf draft create --capability ...` creates the same workspace as the old
command.
- `wf draft add-step --capability ...` adds the same capability-backed step as
the old command.
- `wf draft --help` lists `create` and `add-step`, not the long
`*-from-capability` CLI commands.
- Skills no longer say “there is no `wf draft create --capability` alias.”
- Existing RPC/MCP tests keep passing without programmatic surface renames.
+3 -3
View File
@@ -261,7 +261,7 @@ specific upstream source.
Create a draft from a capability: Create a draft from a capability:
```bash ```bash
wf draft create-from-capability concat_ws wf.std.concat --name concat_ws wf draft create concat_ws --capability wf.std.concat --name concat_ws
``` ```
List and inspect drafts: List and inspect drafts:
@@ -337,14 +337,14 @@ Use `set-route` separately for outcome routing.
### Add A Capability Step To A Draft ### Add A Capability Step To A Draft
Use `wf draft add-step-from-capability` when adding a new capability-backed step Use `wf draft add-step` when adding a new capability-backed step
to an existing draft. The command is explicit: it does not guess missing maps. to an existing draft. The command is explicit: it does not guess missing maps.
When the capability declares multiple outcomes, provide exactly one When the capability declares multiple outcomes, provide exactly one
`--route OUTCOME=TARGET` for each declared outcome. Missing or unknown outcomes `--route OUTCOME=TARGET` for each declared outcome. Missing or unknown outcomes
are rejected before the draft is mutated. are rejected before the draft is mutated.
```bash ```bash
wf draft add-step-from-capability report_ws \ wf draft add-step report_ws \
--revision 3 \ --revision 3 \
--step render \ --step render \
--capability local.report.render_markdown_report \ --capability local.report.render_markdown_report \
+3 -4
View File
@@ -34,8 +34,7 @@ wf cap list --format ids
wf cap inspect <capability> wf cap inspect <capability>
wf cap call <capability> --input '{"field":"value"}' wf cap call <capability> --input '{"field":"value"}'
wf draft create-from-capability <workspace_id> <capability> wf draft create <workspace_id> --capability <capability>
# There is currently no `wf draft create --capability` alias.
wf draft inspect <workspace_id> --include-draft wf draft inspect <workspace_id> --include-draft
wf draft patch <workspace_id> --revision <n> --input-file patch.json wf draft patch <workspace_id> --revision <n> --input-file patch.json
wf draft set-name <workspace_id> --revision <n> --name <name> wf draft set-name <workspace_id> --revision <n> --name <name>
@@ -49,7 +48,7 @@ wf draft handle <workspace_id> --revision <n> --to fail --branch lookup:error --
wf draft compile <workspace_id> wf draft compile <workspace_id>
wf draft bind <workspace_id> --revision <n> --step <step_id> --from local.<field> --to state.<field> wf draft bind <workspace_id> --revision <n> --step <step_id> --from local.<field> --to state.<field>
wf draft bind <workspace_id> --revision <n> --step <step_id> --from input.<field> --to local.<field> wf draft bind <workspace_id> --revision <n> --step <step_id> --from input.<field> --to local.<field>
wf draft add-step-from-capability <workspace_id> --revision <n> --step <step_id> --capability <qualified_name> --from-step <prev> --from-outcome ok --route ok=__end__ --route error=fail --input input.text=text --bind-output result=state.result wf draft add-step <workspace_id> --revision <n> --step <step_id> --capability <qualified_name> --from-step <prev> --from-outcome ok --route ok=__end__ --route error=fail --input input.text=text --bind-output result=state.result
wf draft validate <workspace_id> wf draft validate <workspace_id>
wf draft save <workspace_id> --artifact <artifact_id> --version <n> --title <title> wf draft save <workspace_id> --artifact <artifact_id> --version <n> --title <title>
@@ -88,7 +87,7 @@ 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
`use`; use JSON Patch for non-capability/control draft steps. `use`; use JSON Patch for non-capability/control draft steps.
To add a capability step, prefer `wf draft add-step-from-capability` over raw To add a capability step, prefer `wf draft add-step` over raw
JSON Patch when the route, input bindings, and output-to-state bindings are JSON Patch when the route, input bindings, and output-to-state bindings are
known. It is explicit and does not guess missing maps. known. It is explicit and does not guess missing maps.
If a capability has multiple outcomes, pass one `--route OUTCOME=TARGET` for If a capability has multiple outcomes, pass one `--route OUTCOME=TARGET` for
+1 -1
View File
@@ -18,7 +18,7 @@ low-level escape hatch or you already have a complete compiler/generated plan.
2. Inspect one candidate with `wf cap inspect`. 2. Inspect one candidate with `wf cap inspect`.
3. Call one candidate with `wf cap call` when payload shape or upstream source 3. Call one candidate with `wf cap call` when payload shape or upstream source
reachability is uncertain. reachability is uncertain.
4. Create a patchable draft workspace with `wf draft create-from-capability`. 4. Create a patchable draft workspace with `wf draft create --capability`.
5. Patch targeted fields with focused draft commands or JSON Patch. 5. Patch targeted fields with focused draft commands or JSON Patch.
6. Validate with `wf draft validate`. 6. Validate with `wf draft validate`.
7. Save an artifact with `wf draft save`, or import a complete raw plan with 7. Save an artifact with `wf draft save`, or import a complete raw plan with
@@ -127,8 +127,7 @@ The plan file is the low-level workflow model. It is not a draft workspace.
## Common Mistakes ## Common Mistakes
- Do not use `wf draft create --capability`; current CLI command is - Use `wf draft create <workspace_id> --capability <capability>` for draft creation.
`wf draft create-from-capability <workspace_id> <capability>`.
- Do not pass draft JSON to `artifact create-from-plan`. - Do not pass draft JSON to `artifact create-from-plan`.
- Do not omit deployment bindings for ordinary sources. Platform sources such - Do not omit deployment bindings for ordinary sources. Platform sources such
as `wf.std` may be omitted or self-bound, but configured sources usually need as `wf.std` may be omitted or self-bound, but configured sources usually need
@@ -93,7 +93,7 @@ wf draft handle <workspace_id> --revision <n> --to fail --branch lookup:error --
wf draft compile <workspace_id> wf draft compile <workspace_id>
wf draft bind <workspace_id> --revision <n> --step <step_id> --from local.<field> --to state.<field> wf draft bind <workspace_id> --revision <n> --step <step_id> --from local.<field> --to state.<field>
wf draft bind <workspace_id> --revision <n> --step <step_id> --from input.<field> --to local.<field> wf draft bind <workspace_id> --revision <n> --step <step_id> --from input.<field> --to local.<field>
wf draft add-step-from-capability <workspace_id> --revision <n> --step <step_id> --capability <qualified_name> --from-step <prev> --from-outcome ok --route ok=__end__ --route error=fail --input input.text=text --bind-output result=state.result wf draft add-step <workspace_id> --revision <n> --step <step_id> --capability <qualified_name> --from-step <prev> --from-outcome ok --route ok=__end__ --route error=fail --input input.text=text --bind-output result=state.result
``` ```
`set-input` direction: `input.text=text` means graph source `input.text` maps to `set-input` direction: `input.text=text` means graph source `input.text` maps to
@@ -134,7 +134,7 @@ wf draft validate <workspace_id>
know a map, inspect the capability or run validation rather than guessing. know a map, inspect the capability or run validation rather than guessing.
```bash ```bash
wf draft add-step-from-capability <workspace_id> --revision <n> --step <step_id> --capability <qualified_name> --from-step <prev> --from-outcome ok --route ok=__end__ --route error=fail --input input.text=text --bind-output result=state.result wf draft add-step <workspace_id> --revision <n> --step <step_id> --capability <qualified_name> --from-step <prev> --from-outcome ok --route ok=__end__ --route error=fail --input input.text=text --bind-output result=state.result
wf draft validate <workspace_id> wf draft validate <workspace_id>
``` ```
@@ -41,7 +41,7 @@ Configured sources usually need explicit deployment bindings.
Use the guided draft path when editing step-by-step: Use the guided draft path when editing step-by-step:
```bash ```bash
wf draft create-from-capability <workspace_id> <capability> wf draft create <workspace_id> --capability <capability>
wf draft set-input ... wf draft set-input ...
wf draft set-output ... wf draft set-output ...
wf draft validate <workspace_id> wf draft validate <workspace_id>
@@ -12,7 +12,7 @@ validated, runnable deployment.
3. Inspect one capability. 3. Inspect one capability.
- CLI: `wf cap inspect <name>` - CLI: `wf cap inspect <name>`
4. Bootstrap a draft workspace. 4. Bootstrap a draft workspace.
- CLI: `wf draft create-from-capability <workspace_id> <capability>` - CLI: `wf draft create <workspace_id> --capability <capability>`
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.
@@ -24,7 +24,7 @@ validated, runnable deployment.
the binding in one revision-checked edit. the binding in one revision-checked edit.
- When adding a new capability-backed step, prefer: - When adding a new capability-backed step, prefer:
```bash ```bash
wf draft add-step-from-capability ... wf draft add-step ...
wf draft validate <workspace_id> wf draft validate <workspace_id>
``` ```
Use raw `wf draft patch` only when changing structure that no focused helper Use raw `wf draft patch` only when changing structure that no focused helper
+11 -5
View File
@@ -92,11 +92,17 @@ def inspect_draft(
) )
@app.command("create-from-capability") @app.command("create")
def create_from_capability( def create_from_capability(
ctx: typer.Context, ctx: typer.Context,
workspace_id: Annotated[str, typer.Argument(help="Draft workspace id.")], workspace_id: Annotated[str, typer.Argument(help="Draft workspace id.")],
capability_name: Annotated[str, typer.Argument(help="Workflow capability name.")], capability_name: Annotated[
str,
typer.Option(
"--capability",
help="Qualified capability name used to bootstrap the draft.",
),
],
name: Annotated[ name: Annotated[
str | None, typer.Option("--name", help="Draft workflow name.") str | None, typer.Option("--name", help="Draft workflow name.")
] = None, ] = None,
@@ -104,7 +110,7 @@ def create_from_capability(
str | None, typer.Option("--title", help="Workspace title.") str | None, typer.Option("--title", help="Workspace title.")
] = None, ] = None,
) -> None: ) -> None:
"""Bootstrap a draft workspace from inspect_capability wrapper hints.""" """Create a patchable draft workspace from one capability."""
context = load_cli_context(ctx) context = load_cli_context(ctx)
emit_json( emit_json(
run_cli_operation( run_cli_operation(
@@ -337,7 +343,7 @@ def bind_draft(
) )
@app.command("add-step-from-capability") @app.command("add-step")
def add_step_from_capability( def add_step_from_capability(
ctx: typer.Context, ctx: typer.Context,
workspace_id: Annotated[str, typer.Argument(help="Draft workspace id.")], workspace_id: Annotated[str, typer.Argument(help="Draft workspace id.")],
@@ -384,7 +390,7 @@ def add_step_from_capability(
), ),
] = None, ] = None,
) -> None: ) -> None:
"""Add one capability step with explicit route, input, and output wiring. """Add one capability-backed step with explicit route, input, and output wiring.
This command does not guess missing maps. Pass the route and bindings you This command does not guess missing maps. Pass the route and bindings you
want, then run `wf draft validate <workspace_id>`. want, then run `wf draft validate <workspace_id>`.
+23 -6
View File
@@ -121,11 +121,20 @@ def test_wf_deploy_save_help_exists() -> None:
assert "--binding" in result.output assert "--binding" in result.output
def test_wf_draft_create_from_capability_help_exists() -> None: def test_wf_draft_create_help_accepts_capability_option() -> None:
result = runner.invoke(app, ["draft", "create-from-capability", "--help"]) result = runner.invoke(app, ["draft", "create", "--help"])
assert result.exit_code == 0 assert result.exit_code == 0
assert "--title" in result.output output = " ".join(result.output.split())
assert "--capability" in output
assert "--title" in output
def test_wf_draft_help_does_not_list_old_create_from_capability() -> None:
result = runner.invoke(app, ["draft", "--help"])
assert result.exit_code == 0
assert "create-from-capability" not in result.output
def test_wf_draft_map_help_explains_replace_merge_and_validate() -> None: def test_wf_draft_map_help_explains_replace_merge_and_validate() -> None:
@@ -154,22 +163,30 @@ def test_wf_draft_bind_help_explains_direction() -> None:
assert "validate" in output assert "validate" in output
def test_wf_draft_add_step_from_capability_help_explains_explicit_wiring() -> None: def test_wf_draft_add_step_help_explains_explicit_wiring() -> None:
result = runner.invoke(app, ["draft", "add-step-from-capability", "--help"]) result = runner.invoke(app, ["draft", "add-step", "--help"])
assert result.exit_code == 0 assert result.exit_code == 0
output = " ".join(result.output.split()) output = " ".join(result.output.split())
assert "--capability" in output
assert "--from-step" in output assert "--from-step" in output
assert "--bind-output" in output assert "--bind-output" in output
assert "does not guess" in output assert "does not guess" in output
def test_wf_draft_help_does_not_list_old_add_step_from_capability() -> None:
result = runner.invoke(app, ["draft", "--help"])
assert result.exit_code == 0
assert "add-step-from-capability" not in result.output
def test_wf_draft_route_flags_reject_duplicate_outcomes() -> None: def test_wf_draft_route_flags_reject_duplicate_outcomes() -> None:
add_result = runner.invoke( add_result = runner.invoke(
app, app,
[ [
"draft", "draft",
"add-step-from-capability", "add-step",
"ws", "ws",
"--revision", "--revision",
"1", "1",
+2 -1
View File
@@ -302,8 +302,9 @@ def test_wf_draft_create_patch_validate_save(tmp_path: Path) -> None:
"--config", "--config",
str(config_path), str(config_path),
"draft", "draft",
"create-from-capability", "create",
"echo_workspace", "echo_workspace",
"--capability",
"demo.personal.echo_tool", "demo.personal.echo_tool",
"--name", "--name",
"echo_workspace", "echo_workspace",
+15 -8
View File
@@ -633,8 +633,9 @@ def test_wf_remote_draft_artifact_deploy_lifecycle(monkeypatch, tmp_path) -> Non
[ [
*base_args, *base_args,
"draft", "draft",
"create-from-capability", "create",
"remote_ws", "remote_ws",
"--capability",
"wf.std.constant", "wf.std.constant",
"--name", "--name",
"remote_constant", "remote_constant",
@@ -657,8 +658,9 @@ def test_wf_remote_draft_artifact_deploy_lifecycle(monkeypatch, tmp_path) -> Non
[ [
*base_args, *base_args,
"draft", "draft",
"create-from-capability", "create",
"repair_ws", "repair_ws",
"--capability",
"wf.std.constant", "wf.std.constant",
"--name", "--name",
"repair_constant", "repair_constant",
@@ -927,8 +929,9 @@ def test_wf_draft_delete_succeeds_with_confirm(monkeypatch, tmp_path) -> None:
[ [
*base_args, *base_args,
"draft", "draft",
"create-from-capability", "create",
"delete-me", "delete-me",
"--capability",
"wf.std.constant", "wf.std.constant",
"--name", "--name",
"delete_me_ws", "delete_me_ws",
@@ -1000,8 +1003,9 @@ def test_wf_draft_focused_edit_commands_use_rpc_target(monkeypatch, tmp_path) ->
[ [
*base_args, *base_args,
"draft", "draft",
"create-from-capability", "create",
"focused_ws", "focused_ws",
"--capability",
"wf.std.constant", "wf.std.constant",
"--name", "--name",
"focused_initial", "focused_initial",
@@ -1152,8 +1156,9 @@ def test_wf_draft_bind_uses_rpc_target(monkeypatch, tmp_path) -> None:
[ [
*base_args, *base_args,
"draft", "draft",
"create-from-capability", "create",
"snapshot_ws", "snapshot_ws",
"--capability",
"wf.std.constant", "wf.std.constant",
"--name", "--name",
"snapshot", "snapshot",
@@ -1199,8 +1204,9 @@ def test_wf_draft_add_step_from_capability_uses_rpc_target(
[ [
*base_args, *base_args,
"draft", "draft",
"create-from-capability", "create",
"add_step_ws", "add_step_ws",
"--capability",
"wf.std.constant", "wf.std.constant",
"--name", "--name",
"add_step", "add_step",
@@ -1213,7 +1219,7 @@ def test_wf_draft_add_step_from_capability_uses_rpc_target(
[ [
*base_args, *base_args,
"draft", "draft",
"add-step-from-capability", "add-step",
"add_step_ws", "add_step_ws",
"--revision", "--revision",
"1", "1",
@@ -1253,8 +1259,9 @@ def test_wf_draft_compile_prints_compiled_plan(monkeypatch, tmp_path) -> None:
[ [
*base_args, *base_args,
"draft", "draft",
"create-from-capability", "create",
"compile_ws", "compile_ws",
"--capability",
"wf.std.constant", "wf.std.constant",
"--name", "--name",
"compile_me", "compile_me",