docs: archive completed superpowers plans
This commit is contained in:
@@ -0,0 +1,707 @@
|
||||
# Deployment And Run Next Actions 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:** Add advisory `next_actions` guidance to deployment validation and run lifecycle responses so MCP clients know the safest next workflow tool to call.
|
||||
|
||||
**Architecture:** Extend the existing `src/wf_mcp/workflow_surface/next_actions.py` module with deployment/run constructors instead of putting more UX policy in `handlers.py`. Thread the resulting `NextActions` object into `validate_deployment(...)` and `_run_payload(...)`, which covers `run_deployment`, `resume_run`, `inspect_run`, and `read_run_trace`. Keep diagnostics and runtime status authoritative; `next_actions` remains advisory.
|
||||
|
||||
**Tech Stack:** Python 3.14, Pydantic v2, pytest, ruff, basedpyright.
|
||||
|
||||
---
|
||||
|
||||
## File Structure
|
||||
|
||||
- Modify `src/wf_mcp/workflow_surface/next_actions.py`
|
||||
- Add `NextActions.from_deployment_validation(...)`.
|
||||
- Add `NextActions.from_run_result(...)`.
|
||||
- Add tiny private helpers for diagnostic code inspection and bounded trace examples.
|
||||
|
||||
- Modify `src/wf_mcp/workflow_surface/handlers.py`
|
||||
- Add `next_actions` to `validate_deployment(...)`.
|
||||
- Add optional `next_actions` construction inside `_run_payload(...)`.
|
||||
- Do not duplicate branching logic in individual handler methods.
|
||||
|
||||
- Modify `tests/wf_mcp/workflow_surface/test_next_actions.py`
|
||||
- Unit-test deployment/run constructors directly.
|
||||
|
||||
- Modify `tests/wf_mcp/workflow_surface/test_deployments.py`
|
||||
- Assert `validate_deployment` returns useful next actions for runnable and unrunnable deployments.
|
||||
|
||||
- Modify `tests/wf_mcp/workflow_surface/test_runs.py`
|
||||
- Assert run lifecycle responses return next actions for completed and failed runs.
|
||||
- Add interrupt/resume tests only if an existing fixture/helper makes this small; otherwise leave interrupt-specific coverage to constructor unit tests.
|
||||
|
||||
- Modify `tests/wf_mcp/server/test_config.py`
|
||||
- Assert output schemas for `validate_deployment` and `run_deployment` include `next_actions`.
|
||||
|
||||
- Modify `docs/workflow_capabilities.md`
|
||||
- Add a short note that deployment/run responses now include advisory `next_actions`.
|
||||
|
||||
## Scope Boundaries
|
||||
|
||||
- Do not create new workflow tools.
|
||||
- Do not make `next_actions` required for correctness.
|
||||
- Do not read or return full traces automatically.
|
||||
- Do not add auto-repair behavior.
|
||||
- Do not change existing status strings, diagnostics, or run payload fields.
|
||||
- Do not implement persisted resume changes in this pass.
|
||||
|
||||
---
|
||||
|
||||
### Task 1: Add Constructor Unit Tests
|
||||
|
||||
**Files:**
|
||||
|
||||
- Modify: `tests/wf_mcp/workflow_surface/test_next_actions.py`
|
||||
|
||||
- [ ] **Step 1: Add imports**
|
||||
|
||||
At the top of `tests/wf_mcp/workflow_surface/test_next_actions.py`, add:
|
||||
|
||||
```python
|
||||
from wf_artifacts import DependencyDiagnostic, DiagnosticSeverity
|
||||
```
|
||||
|
||||
Keep the existing `NextActionTool` / `NextActions` import.
|
||||
|
||||
- [ ] **Step 2: Add deployment validation constructor tests**
|
||||
|
||||
Append to `tests/wf_mcp/workflow_surface/test_next_actions.py`:
|
||||
|
||||
```python
|
||||
def test_next_actions_from_runnable_deployment_recommends_run() -> None:
|
||||
actions = NextActions.from_deployment_validation(
|
||||
deployment_id="echo.personal",
|
||||
diagnostics=[],
|
||||
)
|
||||
|
||||
dumped = actions.model_dump(mode="json")
|
||||
assert dumped["can_continue"] is True
|
||||
assert dumped["recommended_next_tool"] == NextActionTool.RUN_DEPLOYMENT.value
|
||||
assert "run_deployment" in dumped["reason"]
|
||||
assert dumped["warnings"] == []
|
||||
|
||||
|
||||
def test_next_actions_from_unrunnable_deployment_recommends_validation_retry() -> None:
|
||||
diagnostic = DependencyDiagnostic(
|
||||
severity=DiagnosticSeverity.ERROR,
|
||||
code="source_unreachable",
|
||||
logical_ref="demo.echo_tool",
|
||||
bound_source="demo.personal",
|
||||
message="Live check for upstream source 'demo.personal' failed.",
|
||||
repair_hint="Start or reconnect the source.",
|
||||
)
|
||||
|
||||
actions = NextActions.from_deployment_validation(
|
||||
deployment_id="echo.personal",
|
||||
diagnostics=[diagnostic],
|
||||
)
|
||||
|
||||
dumped = actions.model_dump(mode="json")
|
||||
assert dumped["can_continue"] is True
|
||||
assert dumped["recommended_next_tool"] == NextActionTool.VALIDATE_DEPLOYMENT.value
|
||||
assert "fix or reconnect" in dumped["reason"]
|
||||
assert dumped["warnings"][0] == "source_unreachable: demo.personal"
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Add run result constructor tests**
|
||||
|
||||
Append to `tests/wf_mcp/workflow_surface/test_next_actions.py`:
|
||||
|
||||
```python
|
||||
def test_next_actions_from_completed_run_has_no_required_next_tool() -> None:
|
||||
actions = NextActions.from_run_result(
|
||||
run_id="run_123",
|
||||
status="completed",
|
||||
trace_count=2,
|
||||
diagnostics=[],
|
||||
)
|
||||
|
||||
dumped = actions.model_dump(mode="json")
|
||||
assert dumped["can_continue"] is False
|
||||
assert dumped["recommended_next_tool"] is None
|
||||
assert "completed" in dumped["reason"]
|
||||
assert dumped["patch_examples"] == []
|
||||
|
||||
|
||||
def test_next_actions_from_failed_run_recommends_bounded_trace() -> None:
|
||||
actions = NextActions.from_run_result(
|
||||
run_id="run_123",
|
||||
status="failed",
|
||||
trace_count=12,
|
||||
diagnostics=[],
|
||||
)
|
||||
|
||||
dumped = actions.model_dump(mode="json")
|
||||
assert dumped["can_continue"] is True
|
||||
assert dumped["recommended_next_tool"] == NextActionTool.READ_RUN_TRACE.value
|
||||
assert "bounded trace" in dumped["reason"]
|
||||
assert dumped["patch_examples"][0]["tool"] == NextActionTool.READ_RUN_TRACE.value
|
||||
assert dumped["patch_examples"][0]["request"]["run_id"] == "run_123"
|
||||
assert dumped["patch_examples"][0]["request"]["trace_range"]["start"] == 0
|
||||
assert dumped["patch_examples"][0]["request"]["trace_range"]["limit"] == 25
|
||||
|
||||
|
||||
def test_next_actions_from_interrupted_run_recommends_resume() -> None:
|
||||
actions = NextActions.from_run_result(
|
||||
run_id="run_123",
|
||||
status="interrupted",
|
||||
trace_count=3,
|
||||
diagnostics=[],
|
||||
)
|
||||
|
||||
dumped = actions.model_dump(mode="json")
|
||||
assert dumped["can_continue"] is True
|
||||
assert dumped["recommended_next_tool"] == NextActionTool.RESUME_RUN.value
|
||||
assert "resume_run" in dumped["reason"]
|
||||
assert dumped["patch_examples"] == []
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Run the new tests to verify they fail**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
uv run pytest tests/wf_mcp/workflow_surface/test_next_actions.py -q
|
||||
```
|
||||
|
||||
Expected: FAIL with `AttributeError` for missing `from_deployment_validation` and `from_run_result`.
|
||||
|
||||
---
|
||||
|
||||
### Task 2: Implement Deployment/Run Constructors
|
||||
|
||||
**Files:**
|
||||
|
||||
- Modify: `src/wf_mcp/workflow_surface/next_actions.py`
|
||||
|
||||
- [ ] **Step 1: Add type-only imports**
|
||||
|
||||
In `src/wf_mcp/workflow_surface/next_actions.py`, add:
|
||||
|
||||
```python
|
||||
from collections.abc import Sequence
|
||||
```
|
||||
|
||||
Do not import `DependencyDiagnostic` directly unless needed at runtime. The constructors can accept diagnostics as objects or dicts to keep coupling low.
|
||||
|
||||
- [ ] **Step 2: Add `from_deployment_validation`**
|
||||
|
||||
Inside `class NextActions`, after `from_wrapper_hints(...)`, add:
|
||||
|
||||
```python
|
||||
@classmethod
|
||||
def from_deployment_validation(
|
||||
cls,
|
||||
*,
|
||||
deployment_id: str,
|
||||
diagnostics: Sequence[object],
|
||||
) -> Self:
|
||||
"""Create guidance after validate_deployment."""
|
||||
if not diagnostics:
|
||||
return cls(
|
||||
can_continue=True,
|
||||
can_save_now=None,
|
||||
recommended_next_tool=NextActionTool.RUN_DEPLOYMENT,
|
||||
reason=(
|
||||
f"Deployment {deployment_id!r} is runnable; call "
|
||||
"wf.workflow.run_deployment with workflow_input."
|
||||
),
|
||||
patch_examples=[],
|
||||
warnings=[],
|
||||
)
|
||||
|
||||
codes = {_diagnostic_field(diagnostic, "code") for diagnostic in diagnostics}
|
||||
warnings = [_diagnostic_warning(diagnostic) for diagnostic in diagnostics]
|
||||
if "source_unreachable" in codes:
|
||||
reason = (
|
||||
"One or more live sources are unreachable; fix or reconnect the "
|
||||
"source, then rerun wf.workflow.validate_deployment with live_check=true."
|
||||
)
|
||||
elif "source_missing" in codes or "binding_missing" in codes:
|
||||
reason = (
|
||||
"Deployment bindings or sources are missing; inspect the deployment "
|
||||
"and save corrected bindings before running."
|
||||
)
|
||||
elif "capability_missing" in codes or "schema_changed" in codes:
|
||||
reason = (
|
||||
"A required capability is missing or drifted; inspect capabilities "
|
||||
"or refresh sources, then validate again."
|
||||
)
|
||||
else:
|
||||
reason = (
|
||||
"Deployment is not runnable; inspect diagnostics, repair the "
|
||||
"deployment or sources, then validate again."
|
||||
)
|
||||
return cls(
|
||||
can_continue=True,
|
||||
can_save_now=None,
|
||||
recommended_next_tool=NextActionTool.VALIDATE_DEPLOYMENT,
|
||||
reason=reason,
|
||||
patch_examples=[],
|
||||
warnings=warnings,
|
||||
)
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Add `from_run_result`**
|
||||
|
||||
Inside `class NextActions`, after `from_deployment_validation(...)`, add:
|
||||
|
||||
```python
|
||||
@classmethod
|
||||
def from_run_result(
|
||||
cls,
|
||||
*,
|
||||
run_id: str | None,
|
||||
status: str,
|
||||
trace_count: int,
|
||||
diagnostics: Sequence[object],
|
||||
) -> Self:
|
||||
"""Create guidance after run_deployment, inspect_run, resume_run, or read_run_trace."""
|
||||
warnings = [_diagnostic_warning(diagnostic) for diagnostic in diagnostics]
|
||||
if status == "interrupted" and run_id is not None:
|
||||
return cls(
|
||||
can_continue=True,
|
||||
can_save_now=None,
|
||||
recommended_next_tool=NextActionTool.RESUME_RUN,
|
||||
reason=(
|
||||
"Run is interrupted; call wf.workflow.resume_run with this "
|
||||
"run_id and the interrupt response payload."
|
||||
),
|
||||
patch_examples=[],
|
||||
warnings=warnings,
|
||||
)
|
||||
if status in {"failed", "unrunnable"}:
|
||||
examples = (
|
||||
[_bounded_trace_example(run_id=run_id, trace_count=trace_count)]
|
||||
if run_id is not None and trace_count > 0
|
||||
else []
|
||||
)
|
||||
return cls(
|
||||
can_continue=bool(examples),
|
||||
can_save_now=None,
|
||||
recommended_next_tool=(
|
||||
NextActionTool.READ_RUN_TRACE if examples else None
|
||||
),
|
||||
reason=(
|
||||
"Run failed; read a bounded trace slice for debugging."
|
||||
if examples
|
||||
else "Run failed before producing trace entries; inspect diagnostics and error."
|
||||
),
|
||||
patch_examples=examples,
|
||||
warnings=warnings,
|
||||
)
|
||||
if status == "completed":
|
||||
examples = (
|
||||
[_bounded_trace_example(run_id=run_id, trace_count=trace_count)]
|
||||
if run_id is not None and trace_count > 0
|
||||
else []
|
||||
)
|
||||
return cls(
|
||||
can_continue=False,
|
||||
can_save_now=None,
|
||||
recommended_next_tool=None,
|
||||
reason=(
|
||||
"Run completed. No required next workflow tool; use read_run_trace "
|
||||
"with a bounded trace_range only if debugging."
|
||||
),
|
||||
patch_examples=examples,
|
||||
warnings=warnings,
|
||||
)
|
||||
return cls(
|
||||
can_continue=False,
|
||||
can_save_now=None,
|
||||
recommended_next_tool=None,
|
||||
reason=f"Run status {status!r} has no obvious next workflow tool.",
|
||||
patch_examples=[],
|
||||
warnings=warnings,
|
||||
)
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Add private diagnostic helpers**
|
||||
|
||||
Below `_wrapper_draft_patch_examples(...)`, add:
|
||||
|
||||
```python
|
||||
def _diagnostic_field(diagnostic: object, field: str) -> str | None:
|
||||
"""Read a diagnostic field from either a Pydantic model or a JSON dict."""
|
||||
if isinstance(diagnostic, dict):
|
||||
value = diagnostic.get(field)
|
||||
else:
|
||||
value = getattr(diagnostic, field, None)
|
||||
return value if isinstance(value, str) else None
|
||||
|
||||
|
||||
def _diagnostic_warning(diagnostic: object) -> str:
|
||||
"""Format one compact diagnostic warning for next_actions."""
|
||||
code = _diagnostic_field(diagnostic, "code") or "diagnostic"
|
||||
bound_source = _diagnostic_field(diagnostic, "bound_source")
|
||||
logical_ref = _diagnostic_field(diagnostic, "logical_ref")
|
||||
if bound_source:
|
||||
return f"{code}: {bound_source}"
|
||||
if logical_ref:
|
||||
return f"{code}: {logical_ref}"
|
||||
return code
|
||||
|
||||
|
||||
def _bounded_trace_example(
|
||||
*,
|
||||
run_id: str,
|
||||
trace_count: int,
|
||||
) -> NextActionPatchExample:
|
||||
"""Return a safe read_run_trace request; never suggest full trace reads."""
|
||||
return NextActionPatchExample(
|
||||
description=(
|
||||
"Read a bounded debug trace slice. Increase start/limit only when needed."
|
||||
),
|
||||
tool=NextActionTool.READ_RUN_TRACE,
|
||||
request={
|
||||
"run_id": run_id,
|
||||
"trace_range": {
|
||||
"start": 0,
|
||||
"limit": min(trace_count, 25),
|
||||
},
|
||||
},
|
||||
)
|
||||
```
|
||||
|
||||
- [ ] **Step 5: Run constructor tests**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
uv run pytest tests/wf_mcp/workflow_surface/test_next_actions.py -q
|
||||
```
|
||||
|
||||
Expected: PASS.
|
||||
|
||||
---
|
||||
|
||||
### Task 3: Thread NextActions Through Deployment Validation
|
||||
|
||||
**Files:**
|
||||
|
||||
- Modify: `src/wf_mcp/workflow_surface/handlers.py`
|
||||
- Modify: `tests/wf_mcp/workflow_surface/test_deployments.py`
|
||||
|
||||
- [ ] **Step 1: Add deployment assertions**
|
||||
|
||||
In `test_workflow_surface_validate_deployment_live_check_is_opt_in`, after current payload assertions, add:
|
||||
|
||||
```python
|
||||
assert payload["next_actions"]["can_continue"] is True
|
||||
assert payload["next_actions"]["recommended_next_tool"] == (
|
||||
"wf.workflow.run_deployment"
|
||||
)
|
||||
```
|
||||
|
||||
In `test_workflow_surface_validates_deployment_dependencies`, after diagnostic assertions, add:
|
||||
|
||||
```python
|
||||
assert payload["next_actions"]["can_continue"] is True
|
||||
assert payload["next_actions"]["recommended_next_tool"] == (
|
||||
"wf.workflow.validate_deployment"
|
||||
)
|
||||
assert payload["next_actions"]["warnings"][0] == "source_missing: context7.personal"
|
||||
```
|
||||
|
||||
If the exact warning uses `context7` instead of `context7.personal`, keep the assertion stable by checking fields separately:
|
||||
|
||||
```python
|
||||
assert payload["next_actions"]["warnings"]
|
||||
assert "source_missing" in payload["next_actions"]["warnings"][0]
|
||||
```
|
||||
|
||||
Prefer the exact assertion only if the implementation returns `bound_source`.
|
||||
|
||||
- [ ] **Step 2: Update `validate_deployment` return payload**
|
||||
|
||||
In `src/wf_mcp/workflow_surface/handlers.py`, replace the return body in `validate_deployment(...)` with:
|
||||
|
||||
```python
|
||||
status = "unrunnable" if diagnostics else "runnable"
|
||||
diagnostic_payloads = [
|
||||
diagnostic.model_dump(mode="json") for diagnostic in diagnostics
|
||||
]
|
||||
return {
|
||||
"deployment_id": deployment.id,
|
||||
"artifact_id": artifact.id,
|
||||
"artifact_version": artifact.version,
|
||||
"status": status,
|
||||
"diagnostics": diagnostic_payloads,
|
||||
"next_actions": NextActions.from_deployment_validation(
|
||||
deployment_id=deployment.id,
|
||||
diagnostics=diagnostics,
|
||||
).model_dump(mode="json"),
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 3: Run deployment tests**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
uv run pytest tests/wf_mcp/workflow_surface/test_deployments.py tests/wf_mcp/workflow_surface/test_next_actions.py -q
|
||||
```
|
||||
|
||||
Expected: PASS.
|
||||
|
||||
---
|
||||
|
||||
### Task 4: Thread NextActions Through Run Payloads
|
||||
|
||||
**Files:**
|
||||
|
||||
- Modify: `src/wf_mcp/workflow_surface/handlers.py`
|
||||
- Modify: `tests/wf_mcp/workflow_surface/test_runs.py`
|
||||
|
||||
- [ ] **Step 1: Add completed-run assertions**
|
||||
|
||||
In `test_workflow_surface_runs_non_interrupting_deployment`, after current run payload assertions, add:
|
||||
|
||||
```python
|
||||
assert payload["next_actions"]["can_continue"] is False
|
||||
assert payload["next_actions"]["recommended_next_tool"] is None
|
||||
assert "completed" in payload["next_actions"]["reason"]
|
||||
```
|
||||
|
||||
After the `inspected` assertions in the same test, add:
|
||||
|
||||
```python
|
||||
assert inspected["next_actions"]["can_continue"] is False
|
||||
assert inspected["next_actions"]["recommended_next_tool"] is None
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Add failed-run assertions**
|
||||
|
||||
In `test_workflow_surface_failed_deployment_exposes_error_on_run_and_inspect`, after current assertions, add:
|
||||
|
||||
```python
|
||||
assert payload["next_actions"]["recommended_next_tool"] is None
|
||||
assert "before producing trace" in payload["next_actions"]["reason"]
|
||||
assert inspected["next_actions"]["recommended_next_tool"] is None
|
||||
```
|
||||
|
||||
This failing artifact currently has `trace_count == 0`, so the safe guidance is diagnostics/error, not `read_run_trace`.
|
||||
|
||||
- [ ] **Step 3: Add trace-detail assertion**
|
||||
|
||||
In `test_workflow_surface_run_deployment_can_include_trace_detail`, after the current trace assertions, add:
|
||||
|
||||
```python
|
||||
assert payload["next_actions"]["patch_examples"][0]["request"]["trace_range"][
|
||||
"limit"
|
||||
] == 1
|
||||
```
|
||||
|
||||
This confirms completed runs may include bounded trace guidance without making it required.
|
||||
|
||||
- [ ] **Step 4: Update `_run_payload`**
|
||||
|
||||
In `src/wf_mcp/workflow_surface/handlers.py`, inside `_run_payload(...)`, add `next_actions` to the base `payload` dict:
|
||||
|
||||
```python
|
||||
"next_actions": NextActions.from_run_result(
|
||||
run_id=run_id,
|
||||
status=status,
|
||||
trace_count=trace_count,
|
||||
diagnostics=diagnostics or [],
|
||||
).model_dump(mode="json"),
|
||||
```
|
||||
|
||||
The resulting base payload should include:
|
||||
|
||||
```python
|
||||
payload = {
|
||||
"deployment_id": deployment.id,
|
||||
"artifact_id": artifact.id,
|
||||
"artifact_version": artifact.version,
|
||||
"status": status,
|
||||
"run_id": run_id,
|
||||
"resume_readiness": resume_readiness,
|
||||
"interrupt": interrupt,
|
||||
"outcome": outcome,
|
||||
"error": error,
|
||||
"output": output,
|
||||
"diagnostics": [
|
||||
diagnostic.model_dump(mode="json") for diagnostic in diagnostics or []
|
||||
],
|
||||
"trace_count": trace_count,
|
||||
"next_actions": NextActions.from_run_result(
|
||||
run_id=run_id,
|
||||
status=status,
|
||||
trace_count=trace_count,
|
||||
diagnostics=diagnostics or [],
|
||||
).model_dump(mode="json"),
|
||||
}
|
||||
```
|
||||
|
||||
- [ ] **Step 5: Run run tests**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
uv run pytest tests/wf_mcp/workflow_surface/test_runs.py tests/wf_mcp/workflow_surface/test_next_actions.py -q
|
||||
```
|
||||
|
||||
Expected: PASS.
|
||||
|
||||
---
|
||||
|
||||
### Task 5: Update MCP Output Schema Tests
|
||||
|
||||
**Files:**
|
||||
|
||||
- Modify: `tests/wf_mcp/server/test_config.py`
|
||||
- Modify: `tests/wf_mcp/server/test_tools.py`
|
||||
|
||||
- [ ] **Step 1: Add schema assertions for deployment/run outputs**
|
||||
|
||||
In `tests/wf_mcp/server/test_config.py`, find the existing workflow-surface schema test that checks `create_draft_workspace_from_capability` output contains `next_actions`.
|
||||
|
||||
Add assertions for `wf.workflow.validate_deployment` and `wf.workflow.run_deployment` output schemas. Use the same local `by_name` / tool lookup style already in that test file:
|
||||
|
||||
```python
|
||||
validate_deployment = by_name["wf.workflow.validate_deployment"]
|
||||
run_deployment = by_name["wf.workflow.run_deployment"]
|
||||
|
||||
validate_output = validate_deployment.outputSchema
|
||||
run_output = run_deployment.outputSchema
|
||||
|
||||
assert "next_actions" in validate_output["properties"]
|
||||
assert "recommended_next_tool" in validate_output["properties"]["next_actions"][
|
||||
"properties"
|
||||
]
|
||||
assert "next_actions" in run_output["properties"]
|
||||
assert "recommended_next_tool" in run_output["properties"]["next_actions"][
|
||||
"properties"
|
||||
]
|
||||
```
|
||||
|
||||
If this test file uses `tool.outputSchema` through dict access instead of attributes, follow the existing style in the file. Do not assert the whole schema.
|
||||
|
||||
- [ ] **Step 2: Add tool description assertion only if output schema exists**
|
||||
|
||||
In `tests/wf_mcp/server/test_tools.py`, add a small assertion that `run_deployment.description` or the output schema describes `next_actions` only if that file already inspects output schemas. Do not add fragile full-schema assertions.
|
||||
|
||||
If `test_tools.py` only checks input schemas and titles, skip this step.
|
||||
|
||||
- [ ] **Step 3: Run server tests**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
uv run pytest tests/wf_mcp/server/test_config.py tests/wf_mcp/server/test_tools.py -q
|
||||
```
|
||||
|
||||
Expected: PASS.
|
||||
|
||||
---
|
||||
|
||||
### Task 6: Update Docs
|
||||
|
||||
**Files:**
|
||||
|
||||
- Modify: `docs/workflow_capabilities.md`
|
||||
|
||||
- [ ] **Step 1: Add deployment/run guidance note**
|
||||
|
||||
Add this paragraph near the existing `next_actions` explanation:
|
||||
|
||||
```markdown
|
||||
Deployment validation and run lifecycle responses also expose `next_actions`.
|
||||
For runnable deployments this points to `wf.workflow.run_deployment`; for
|
||||
unrunnable deployments it points back to validation after the caller repairs
|
||||
bindings, sources, or schema drift. Failed runs never suggest reading an
|
||||
unbounded trace; trace guidance always uses a bounded `trace_range`.
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Run docs tests**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
uv run pytest tests/wf_mcp/server/test_docs.py -q
|
||||
```
|
||||
|
||||
Expected: PASS.
|
||||
|
||||
---
|
||||
|
||||
### Task 7: Verification
|
||||
|
||||
**Files:**
|
||||
|
||||
- All touched files.
|
||||
|
||||
- [ ] **Step 1: Run focused test set**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
uv run pytest tests/wf_mcp/workflow_surface/test_next_actions.py tests/wf_mcp/workflow_surface/test_deployments.py tests/wf_mcp/workflow_surface/test_runs.py tests/wf_mcp/server/test_config.py tests/wf_mcp/server/test_tools.py tests/wf_mcp/server/test_docs.py -q
|
||||
```
|
||||
|
||||
Expected: PASS.
|
||||
|
||||
- [ ] **Step 2: Run formatting check on touched files**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
uv run ruff format --check src/wf_mcp/workflow_surface/next_actions.py src/wf_mcp/workflow_surface/handlers.py tests/wf_mcp/workflow_surface/test_next_actions.py tests/wf_mcp/workflow_surface/test_deployments.py tests/wf_mcp/workflow_surface/test_runs.py tests/wf_mcp/server/test_config.py tests/wf_mcp/server/test_tools.py
|
||||
```
|
||||
|
||||
Expected: PASS.
|
||||
|
||||
- [ ] **Step 3: Run lint on touched files**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
uv run ruff check src/wf_mcp/workflow_surface/next_actions.py src/wf_mcp/workflow_surface/handlers.py tests/wf_mcp/workflow_surface/test_next_actions.py tests/wf_mcp/workflow_surface/test_deployments.py tests/wf_mcp/workflow_surface/test_runs.py tests/wf_mcp/server/test_config.py tests/wf_mcp/server/test_tools.py
|
||||
```
|
||||
|
||||
Expected: PASS.
|
||||
|
||||
- [ ] **Step 4: Run type check**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
uv run basedpyright --level error
|
||||
```
|
||||
|
||||
Expected: `0 errors`.
|
||||
|
||||
- [ ] **Step 5: Optional full suite**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
uv run pytest -q
|
||||
```
|
||||
|
||||
Expected: full suite remains green with the existing skip/xfail count.
|
||||
|
||||
---
|
||||
|
||||
## Self-Review Checklist
|
||||
|
||||
- `next_actions` is present on `validate_deployment` responses.
|
||||
- `next_actions` is present on all `_run_payload(...)` responses.
|
||||
- Completed runs do not require another tool.
|
||||
- Failed runs recommend bounded trace only when a trace exists.
|
||||
- Interrupted runs recommend `wf.workflow.resume_run`.
|
||||
- Diagnostics remain authoritative; guidance only summarizes.
|
||||
- No unbounded trace read is suggested.
|
||||
- Existing fields remain unchanged.
|
||||
|
||||
## Notes For Opencode
|
||||
|
||||
- Keep this as an additive UX change.
|
||||
- Do not make any runtime/deployment behavior depend on `next_actions`.
|
||||
- Do not add a new tool.
|
||||
- Do not broaden trace payloads.
|
||||
- If server schema tests are awkward, prefer field-level assertions over whole-schema equality.
|
||||
Reference in New Issue
Block a user