docs: archive completed superpowers plans
This commit is contained in:
@@ -0,0 +1,699 @@
|
||||
# Workflow Draft Surface 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 the disposable draft prototype with the first real MCP-facing workflow draft surface: keyed, patchable JSON that adapts into `wf_authoring.WorkflowBuilder` instead of rebuilding graph semantics itself.
|
||||
|
||||
**Architecture:** The new draft layer remains a typed JSON seam in `wf_artifacts`, but delegates graph construction to `wf_authoring`. Draft parsing owns keyed presentation, validation, and stable patch paths; `WorkflowBuilder` owns graph construction. The MCP workflow surface keeps the same tool family while accepting the new draft shape.
|
||||
|
||||
**Tech Stack:** Python 3.14, Pydantic v2, `wf_authoring`, `wf_core`, `jsonpatch`, pytest, basedpyright, Ruff.
|
||||
|
||||
---
|
||||
|
||||
## File Structure
|
||||
|
||||
### Create
|
||||
|
||||
- `src/wf_artifacts/drafts/models.py`
|
||||
- concrete Pydantic draft document models
|
||||
- `src/wf_artifacts/drafts/adapter.py`
|
||||
- thin JSON-draft-to-`WorkflowBuilder` adapter
|
||||
- `src/wf_artifacts/drafts/api.py`
|
||||
- public compile/validate/patch functions and diagnostics
|
||||
- `tests/artifacts/test_draft_models.py`
|
||||
- draft document validation
|
||||
- `tests/artifacts/test_draft_adapter.py`
|
||||
- keyed step/routes lowering through `WorkflowBuilder`
|
||||
- `tests/artifacts/test_draft_api.py`
|
||||
- public compile/validate/patch behavior
|
||||
|
||||
### Modify
|
||||
|
||||
- `src/wf_artifacts/drafts.py`
|
||||
- replace module body with compatibility re-exports or remove once imports are updated
|
||||
- `src/wf_artifacts/__init__.py`
|
||||
- export public draft API/models from the package
|
||||
- `src/wf_authoring/builder/core.py`
|
||||
- add `use_ref(...)` for named external capabilities without local `NodeSpec`s
|
||||
- `src/wf_authoring/ops/*`
|
||||
- only if route helpers need a reusable public lowering entrypoint
|
||||
- `src/wf_mcp/workflow_surface/handlers.py`
|
||||
- keep using public draft API; no semantic duplication
|
||||
- `tests/wf_mcp/test_workflow_surface.py`
|
||||
- update draft fixtures to the new keyed document shape
|
||||
- `tests/wf_mcp/test_server.py`
|
||||
- confirm MCP schemas remain plain-object friendly
|
||||
- `docs/workflow_drafts.md`
|
||||
- replace prototype examples with the first real draft surface
|
||||
- `docs/wf_mcp_end_to_end_runbook.md`
|
||||
- update draft example
|
||||
- `docs/wf_mcp_operator_manual.md`
|
||||
- keep draft-first guidance accurate
|
||||
- `docs/wf_mcp_troubleshooting.md`
|
||||
- update patch-path examples
|
||||
|
||||
## Task 1: Split Draft Code Into Focused Modules
|
||||
|
||||
**Files:**
|
||||
|
||||
- Create: `src/wf_artifacts/drafts/models.py`
|
||||
- Create: `src/wf_artifacts/drafts/api.py`
|
||||
- Create: `src/wf_artifacts/drafts/adapter.py`
|
||||
- Modify: `src/wf_artifacts/drafts.py`
|
||||
- Modify: `src/wf_artifacts/__init__.py`
|
||||
- Test: `tests/artifacts/test_draft_models.py`
|
||||
|
||||
- [ ] **Step 1: Write the failing model tests**
|
||||
|
||||
```python
|
||||
from wf_artifacts.drafts import DraftUseStep, WorkflowDraft
|
||||
|
||||
|
||||
def test_workflow_draft_uses_keyed_steps() -> None:
|
||||
draft = WorkflowDraft.model_validate(
|
||||
{
|
||||
"name": "echo",
|
||||
"input_schema": {},
|
||||
"state_schema": {"fields": {}},
|
||||
"output_schema": {},
|
||||
"start": "echo",
|
||||
"steps": {
|
||||
"echo": {
|
||||
"use": "demo.echo",
|
||||
"in": {"input.text": "text"},
|
||||
"out": {"echoed": "state.echoed"},
|
||||
}
|
||||
},
|
||||
"routes": {"echo": {"ok": "__end__"}},
|
||||
}
|
||||
)
|
||||
|
||||
assert isinstance(draft.steps["echo"], DraftUseStep)
|
||||
assert draft.steps["echo"].use == "demo.echo"
|
||||
|
||||
|
||||
def test_draft_step_requires_exactly_one_kind_key() -> None:
|
||||
result = WorkflowDraft.model_validate(
|
||||
{
|
||||
"name": "bad",
|
||||
"input_schema": {},
|
||||
"state_schema": {"fields": {}},
|
||||
"output_schema": {},
|
||||
"start": "bad",
|
||||
"steps": {
|
||||
"bad": {
|
||||
"use": "demo.echo",
|
||||
"join": {},
|
||||
}
|
||||
},
|
||||
"routes": {},
|
||||
}
|
||||
)
|
||||
```
|
||||
|
||||
The second test should be written with `pytest.raises(ValidationError)` and assert the authoring path identifies `steps.bad`.
|
||||
|
||||
- [ ] **Step 2: Run tests to verify they fail**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
uv run --with pytest pytest tests/artifacts/test_draft_models.py -q
|
||||
```
|
||||
|
||||
Expected: import errors or validation failures because keyed draft models do not exist yet.
|
||||
|
||||
- [ ] **Step 3: Implement minimal concrete draft models**
|
||||
|
||||
Create:
|
||||
|
||||
```python
|
||||
# src/wf_artifacts/drafts/models.py
|
||||
from __future__ import annotations
|
||||
|
||||
from typing import Annotated, Any, Literal
|
||||
|
||||
from pydantic import BaseModel, Field, model_validator
|
||||
|
||||
JsonObject = dict[str, Any]
|
||||
STEP_KIND_KEYS = frozenset({"use", "foreach", "interrupt", "join"})
|
||||
|
||||
|
||||
class DraftUseStep(BaseModel):
|
||||
use: str
|
||||
in_: dict[str, str] = Field(default_factory=dict, alias="in")
|
||||
out: dict[str, str] = Field(default_factory=dict)
|
||||
desc: str | None = None
|
||||
retry: int | None = Field(default=None, ge=0)
|
||||
timeout_seconds: int | None = Field(default=None, gt=0)
|
||||
|
||||
|
||||
class DraftForeachPayload(BaseModel):
|
||||
over: str
|
||||
as_: str = Field(alias="as")
|
||||
mode: Literal["serial", "parallel"] = "serial"
|
||||
on_item_error: Literal["fail", "collect", "skip"] = "fail"
|
||||
|
||||
|
||||
class DraftForeachStep(BaseModel):
|
||||
foreach: DraftForeachPayload
|
||||
|
||||
|
||||
class DraftInterruptPayload(BaseModel):
|
||||
kind: str
|
||||
request: dict[str, str] = Field(default_factory=dict)
|
||||
resume: dict[str, str] = Field(default_factory=dict)
|
||||
outcomes: list[str] = Field(default_factory=lambda: ["submitted"])
|
||||
|
||||
|
||||
class DraftInterruptStep(BaseModel):
|
||||
interrupt: DraftInterruptPayload
|
||||
|
||||
|
||||
class DraftJoinStep(BaseModel):
|
||||
join: JsonObject = Field(default_factory=dict)
|
||||
|
||||
|
||||
DraftStep = Annotated[
|
||||
DraftUseStep
|
||||
| DraftForeachStep
|
||||
| DraftInterruptStep
|
||||
| DraftJoinStep,
|
||||
Field(discriminator=None),
|
||||
]
|
||||
|
||||
|
||||
class WorkflowDraft(BaseModel):
|
||||
name: str
|
||||
input_schema: JsonObject
|
||||
state_schema: JsonObject
|
||||
output_schema: JsonObject
|
||||
start: str
|
||||
steps: dict[str, DraftStep]
|
||||
routes: dict[str, dict[str, str]] = Field(default_factory=dict)
|
||||
|
||||
@model_validator(mode="before")
|
||||
@classmethod
|
||||
def _validate_step_kinds(cls, value: object) -> object:
|
||||
if not isinstance(value, dict):
|
||||
return value
|
||||
steps = value.get("steps")
|
||||
if not isinstance(steps, dict):
|
||||
return value
|
||||
for step_id, payload in steps.items():
|
||||
if not isinstance(payload, dict):
|
||||
continue
|
||||
present = STEP_KIND_KEYS.intersection(payload)
|
||||
if len(present) != 1:
|
||||
raise ValueError(
|
||||
f"steps.{step_id} must contain exactly one step kind key"
|
||||
)
|
||||
return value
|
||||
```
|
||||
|
||||
Keep the public import path stable by re-exporting through `src/wf_artifacts/drafts.py` during the transition.
|
||||
|
||||
- [ ] **Step 4: Run tests to verify they pass**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
uv run --with pytest pytest tests/artifacts/test_draft_models.py -q
|
||||
```
|
||||
|
||||
Expected: pass.
|
||||
|
||||
- [ ] **Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add src/wf_artifacts tests/artifacts/test_draft_models.py
|
||||
git commit -m "refactor: add keyed workflow draft models"
|
||||
```
|
||||
|
||||
## Task 2: Add `use_ref` And Thin Adapter Over `WorkflowBuilder`
|
||||
|
||||
**Files:**
|
||||
|
||||
- Create: `src/wf_artifacts/drafts/adapter.py`
|
||||
- Modify only if needed: `src/wf_authoring/builder/core.py`
|
||||
- Modify only if needed: `src/wf_authoring/ops/*`
|
||||
- Test: `tests/artifacts/test_draft_adapter.py`
|
||||
|
||||
- [ ] **Step 1: Write failing adapter tests**
|
||||
|
||||
```python
|
||||
from wf_artifacts.drafts import WorkflowDraft
|
||||
from wf_artifacts.drafts.adapter import build_workflow_from_draft
|
||||
|
||||
|
||||
def test_adapter_lowers_keyed_use_steps_and_routes_through_builder() -> None:
|
||||
draft = WorkflowDraft.model_validate(
|
||||
{
|
||||
"name": "echo",
|
||||
"input_schema": {},
|
||||
"state_schema": {"fields": {}},
|
||||
"output_schema": {},
|
||||
"start": "echo",
|
||||
"steps": {"echo": {"use": "demo.echo"}},
|
||||
"routes": {"echo": {"ok": "__end__"}},
|
||||
}
|
||||
)
|
||||
|
||||
workflow = build_workflow_from_draft(draft)
|
||||
|
||||
assert workflow.nodes[0].id == "echo"
|
||||
assert workflow.nodes[0].node == "demo.echo"
|
||||
assert workflow.edges[0].from_ == "echo"
|
||||
assert workflow.edges[0].outcome == "ok"
|
||||
assert workflow.edges[0].to == "__end__"
|
||||
|
||||
|
||||
def test_builder_use_ref_creates_external_node_use_without_node_def() -> None:
|
||||
builder = WorkflowBuilder(
|
||||
"echo",
|
||||
input_schema={},
|
||||
state_schema={"fields": {}},
|
||||
output_schema={},
|
||||
)
|
||||
|
||||
step = builder.use_ref("demo.echo", id="echo")
|
||||
builder.set_entry_point(step)
|
||||
builder.connect(step, "ok", "__end__")
|
||||
workflow = builder.compile()
|
||||
|
||||
assert step.node == "demo.echo"
|
||||
assert workflow.node_defs == []
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Run tests to verify they fail**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
uv run --with pytest pytest tests/artifacts/test_draft_adapter.py -q
|
||||
```
|
||||
|
||||
Expected: import error because `build_workflow_from_draft` does not exist.
|
||||
|
||||
- [ ] **Step 3: Implement the thin adapter**
|
||||
|
||||
First add:
|
||||
|
||||
```python
|
||||
def use_ref(
|
||||
self,
|
||||
name: str,
|
||||
*,
|
||||
id: str | None = None,
|
||||
in_map: MapArg | None = None,
|
||||
out_map: MapArg | None = None,
|
||||
desc: str | None = None,
|
||||
) -> NodeUse:
|
||||
...
|
||||
```
|
||||
|
||||
`use_ref` creates a `NodeUse` for an already named external capability and does
|
||||
not add a local `NodeDef`.
|
||||
|
||||
Then implement `build_workflow_from_draft(draft: WorkflowDraft) -> Workflow` so
|
||||
it:
|
||||
|
||||
1. constructs a `WorkflowBuilder`
|
||||
2. registers each draft step by stable id
|
||||
3. uses existing `WorkflowBuilder` public methods for:
|
||||
- `use_ref`
|
||||
- `foreach`
|
||||
- `interrupt`
|
||||
- `join`
|
||||
4. applies `routes`
|
||||
5. calls explicit `start(...)`
|
||||
6. returns `builder.build(...)`
|
||||
|
||||
Do **not** invent draft route sugar in this pass.
|
||||
|
||||
- [ ] **Step 4: Run tests to verify they pass**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
uv run --with pytest pytest tests/artifacts/test_draft_adapter.py -q
|
||||
```
|
||||
|
||||
Expected: pass.
|
||||
|
||||
- [ ] **Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add src/wf_artifacts src/wf_authoring tests/artifacts/test_draft_adapter.py
|
||||
git commit -m "feat: adapt workflow drafts through workflow builder"
|
||||
```
|
||||
|
||||
## Task 3: Replace Prototype Public API
|
||||
|
||||
**Files:**
|
||||
|
||||
- Create: `src/wf_artifacts/drafts/api.py`
|
||||
- Modify: `src/wf_artifacts/drafts.py`
|
||||
- Modify: `src/wf_artifacts/__init__.py`
|
||||
- Test: `tests/artifacts/test_draft_api.py`
|
||||
|
||||
- [ ] **Step 1: Write failing API tests**
|
||||
|
||||
```python
|
||||
from wf_artifacts.drafts import compile_workflow_draft, patch_workflow_draft
|
||||
|
||||
|
||||
def test_compile_workflow_draft_returns_raw_core_shape() -> None:
|
||||
plan = compile_workflow_draft(_keyed_echo_draft())
|
||||
|
||||
assert plan["nodes"][0]["id"] == "echo"
|
||||
assert plan["nodes"][0]["node"] == "demo.echo"
|
||||
assert plan["edges"][0]["outcome"] == "ok"
|
||||
|
||||
|
||||
def test_patch_workflow_draft_uses_stable_step_paths() -> None:
|
||||
result = patch_workflow_draft(
|
||||
_keyed_echo_draft(),
|
||||
[
|
||||
{
|
||||
"op": "replace",
|
||||
"path": "/steps/echo/in/input.text",
|
||||
"value": "message",
|
||||
}
|
||||
],
|
||||
)
|
||||
|
||||
assert result["status"] == "valid"
|
||||
assert result["draft"]["steps"]["echo"]["in"]["input.text"] == "message"
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Run tests to verify they fail**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
uv run --with pytest pytest tests/artifacts/test_draft_api.py -q
|
||||
```
|
||||
|
||||
Expected: failures because the old prototype API still expects array `steps`.
|
||||
|
||||
- [ ] **Step 3: Implement the API**
|
||||
|
||||
Create:
|
||||
|
||||
```python
|
||||
# src/wf_artifacts/drafts/api.py
|
||||
def compile_workflow_draft(draft: JsonObject) -> JsonObject:
|
||||
parsed = WorkflowDraft.model_validate(draft)
|
||||
workflow = build_workflow_from_draft(parsed)
|
||||
return workflow.model_dump(mode="json", by_alias=True, exclude={"node_defs"})
|
||||
```
|
||||
|
||||
Keep:
|
||||
|
||||
- `validate_workflow_draft`
|
||||
- `patch_workflow_draft`
|
||||
- structured `DraftDiagnostic`
|
||||
|
||||
Update diagnostics to use keyed paths such as:
|
||||
|
||||
```text
|
||||
steps.echo.in
|
||||
routes.echo.error
|
||||
```
|
||||
|
||||
Delete the old array-step prototype code after public tests are green.
|
||||
|
||||
- [ ] **Step 4: Run tests to verify they pass**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
uv run --with pytest pytest tests/artifacts/test_draft_api.py -q
|
||||
```
|
||||
|
||||
Expected: pass.
|
||||
|
||||
- [ ] **Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add src/wf_artifacts tests/artifacts/test_draft_api.py
|
||||
git commit -m "feat: replace draft prototype with keyed public api"
|
||||
```
|
||||
|
||||
## Task 4: Update MCP Workflow Surface
|
||||
|
||||
**Files:**
|
||||
|
||||
- Modify: `tests/wf_mcp/test_workflow_surface.py`
|
||||
- Modify: `tests/wf_mcp/test_server.py`
|
||||
- Modify only if needed: `src/wf_mcp/workflow_surface/handlers.py`
|
||||
|
||||
- [ ] **Step 1: Update the failing MCP tests**
|
||||
|
||||
Replace old fixtures like:
|
||||
|
||||
```python
|
||||
"steps": [{"id": "echo", "kind": "use", ...}]
|
||||
```
|
||||
|
||||
with:
|
||||
|
||||
```python
|
||||
"steps": {"echo": {"use": "demo.echo", ...}},
|
||||
"routes": {"echo": {"ok": "__end__"}},
|
||||
```
|
||||
|
||||
Keep assertions that:
|
||||
|
||||
- draft tools still expose plain object schemas to MCP clients
|
||||
- `create_artifact_from_draft` still saves artifacts
|
||||
- source binding normalization still works
|
||||
- missing `wf.std` self-binding diagnostics still work
|
||||
|
||||
- [ ] **Step 2: Run tests to verify failures**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
uv run --with pytest pytest tests/wf_mcp/test_workflow_surface.py tests/wf_mcp/test_server.py -q
|
||||
```
|
||||
|
||||
Expected: failures wherever MCP handlers still assume the old prototype shape.
|
||||
|
||||
- [ ] **Step 3: Make minimal MCP adjustments**
|
||||
|
||||
Keep handlers thin:
|
||||
|
||||
```python
|
||||
plan = compile_workflow_draft(draft)
|
||||
```
|
||||
|
||||
No duplicate draft interpretation should appear in `wf_mcp`.
|
||||
|
||||
- [ ] **Step 4: Run tests to verify they pass**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
uv run --with pytest pytest tests/wf_mcp/test_workflow_surface.py tests/wf_mcp/test_server.py -q
|
||||
```
|
||||
|
||||
Expected: pass.
|
||||
|
||||
- [ ] **Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add src/wf_mcp tests/wf_mcp
|
||||
git commit -m "feat: accept keyed workflow drafts over mcp"
|
||||
```
|
||||
|
||||
## Task 5: Add Outcome Validation When Capability Contracts Are Available
|
||||
|
||||
**Files:**
|
||||
|
||||
- Modify: `src/wf_artifacts/drafts/api.py`
|
||||
- Modify: `src/wf_mcp/workflow_surface/handlers.py`
|
||||
- Test: `tests/wf_mcp/test_workflow_surface.py`
|
||||
|
||||
- [ ] **Step 1: Write failing outcome validation test**
|
||||
|
||||
```python
|
||||
def test_draft_validation_rejects_unknown_capability_outcome_when_spec_is_known() -> None:
|
||||
handlers = _handlers_with_demo_echo_spec()
|
||||
draft = _keyed_echo_draft()
|
||||
draft["routes"]["echo"] = {"typo": "__end__"}
|
||||
|
||||
result = asyncio.run(handlers.validate_draft(draft=draft))
|
||||
|
||||
assert result["status"] == "invalid"
|
||||
assert result["diagnostics"][0]["path"] == "routes.echo.typo"
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Run test to verify it fails**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
uv run --with pytest pytest tests/wf_mcp/test_workflow_surface.py -q
|
||||
```
|
||||
|
||||
Expected: validation currently accepts the typo.
|
||||
|
||||
- [ ] **Step 3: Implement capability-aware outcome validation**
|
||||
|
||||
Pass an optional capability lookup into draft validation from MCP handlers.
|
||||
|
||||
Rules:
|
||||
|
||||
- validate outcome keys for `use` steps only when the capability is resolvable
|
||||
- if the capability is unknown/unavailable, leave dependency validation to the
|
||||
later artifact/deployment stages
|
||||
- diagnostic path must identify the keyed route entry
|
||||
|
||||
Do not make `wf_artifacts` depend on `wf_mcp`; define a tiny callable/protocol
|
||||
interface for lookup instead.
|
||||
|
||||
- [ ] **Step 4: Run tests to verify they pass**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
uv run --with pytest pytest tests/wf_mcp/test_workflow_surface.py -q
|
||||
```
|
||||
|
||||
Expected: pass.
|
||||
|
||||
- [ ] **Step 5: Commit**
|
||||
|
||||
```bash
|
||||
git add src/wf_artifacts src/wf_mcp tests/wf_mcp/test_workflow_surface.py
|
||||
git commit -m "feat: validate draft routes against known outcomes"
|
||||
```
|
||||
|
||||
## Task 6: Update Documentation
|
||||
|
||||
**Files:**
|
||||
|
||||
- Modify: `docs/workflow_drafts.md`
|
||||
- Modify: `docs/wf_mcp_end_to_end_runbook.md`
|
||||
- Modify: `docs/wf_mcp_operator_manual.md`
|
||||
- Modify: `docs/wf_mcp_troubleshooting.md`
|
||||
|
||||
- [ ] **Step 1: Update docs to the real draft surface**
|
||||
|
||||
Replace prototype array examples with keyed examples:
|
||||
|
||||
```json
|
||||
"steps": {
|
||||
"echo": {
|
||||
"use": "demo.echo_tool",
|
||||
"in": {"input.text": "text"},
|
||||
"out": {"echoed": "state.echoed"}
|
||||
}
|
||||
},
|
||||
"routes": {
|
||||
"echo": {
|
||||
"ok": "__end__"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Document:
|
||||
|
||||
- exactly-one-kind-key rule
|
||||
- stable keyed patch paths
|
||||
- `route` as repeated condition-chain sugar
|
||||
- `WorkflowBuilder` as the semantic owner beneath the JSON adapter
|
||||
- outcome strings validated against capability contracts when available
|
||||
|
||||
- [ ] **Step 2: Run a targeted docs scan**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
rg -n '\"steps\": \\[|\"kind\": \"use\"|/steps/0|create_artifact_from_draft' docs
|
||||
```
|
||||
|
||||
Expected:
|
||||
|
||||
- no stale prototype examples in current docs
|
||||
- `create_artifact_from_draft` still documented as the preferred path
|
||||
|
||||
- [ ] **Step 3: Commit**
|
||||
|
||||
```bash
|
||||
git add docs
|
||||
git commit -m "docs: describe keyed workflow draft surface"
|
||||
```
|
||||
|
||||
## Task 7: Full Verification
|
||||
|
||||
**Files:**
|
||||
|
||||
- No new files
|
||||
|
||||
- [ ] **Step 1: Run focused verification**
|
||||
|
||||
```bash
|
||||
uv run --with pytest pytest tests/artifacts tests/wf_mcp/test_workflow_surface.py tests/wf_mcp/test_server.py -q
|
||||
```
|
||||
|
||||
Expected: pass.
|
||||
|
||||
- [ ] **Step 2: Run full project tests**
|
||||
|
||||
```bash
|
||||
uv run --with pytest pytest -q
|
||||
```
|
||||
|
||||
Expected: pass.
|
||||
|
||||
- [ ] **Step 3: Run type checking**
|
||||
|
||||
```bash
|
||||
uv run basedpyright --level error
|
||||
```
|
||||
|
||||
Expected: `0 errors`.
|
||||
|
||||
- [ ] **Step 4: Run lint**
|
||||
|
||||
```bash
|
||||
uvx ruff check src tests
|
||||
```
|
||||
|
||||
Expected: pass.
|
||||
|
||||
- [ ] **Step 5: Commit final cleanup**
|
||||
|
||||
```bash
|
||||
git add .
|
||||
git commit -m "feat: ship keyed workflow draft authoring surface"
|
||||
```
|
||||
|
||||
## Self-Review
|
||||
|
||||
### Spec Coverage
|
||||
|
||||
- keyed `steps`: Tasks 1-4
|
||||
- compact `routes`: Tasks 1-4
|
||||
- verb-keyed explicit step kinds: Task 1
|
||||
- saved capability/workflow refs in `use`: Task 2, existing capability refs pass through unchanged
|
||||
- stable patch paths: Tasks 3 and 6
|
||||
- `wf_authoring` as semantic owner: Tasks 2 and 6
|
||||
- outcome validation against declared contracts: Task 5
|
||||
- prototype replacement rather than migration: Tasks 3, 4, 6
|
||||
|
||||
### Placeholder Scan
|
||||
|
||||
- no `TBD`
|
||||
- no unspecified "add validation" placeholders
|
||||
- every task has exact files, tests, commands, and expected behavior
|
||||
|
||||
### Type Consistency
|
||||
|
||||
- `WorkflowDraft`, `DraftUseStep`, `build_workflow_from_draft`, and public API
|
||||
names stay consistent across all tasks
|
||||
- patch examples use keyed paths consistently
|
||||
- `routes` stays the only authored outcome-routing section
|
||||
Reference in New Issue
Block a user