docs: refresh roadmap and status plan

This commit is contained in:
lda
2026-06-08 23:16:17 +07:00 Verified
parent 5b81380f2a
commit 04122e12ac
2 changed files with 604 additions and 509 deletions
@@ -0,0 +1,502 @@
# wf Status And Product Smoke 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 a read-only `wf status` command that tells users what target they are connected to and summarizes server/source/admin state, then add a small product smoke test path for the remote server workflow.
**Architecture:** Do not add a new backend status endpoint in this slice. The CLI should compose status from existing surfaces already available on `CliContext`: workflow capability list, source inventory, admin connection/status/events/auth summaries, and source registry summaries when available. Local/static servers and remote RPC servers should both work; unavailable optional admin surfaces should report `available: false` instead of failing the whole command.
**Tech Stack:** Python 3.14, Typer CLI, existing `WorkflowApiSurface` / admin surface protocols, JSON output via `wf_cli.io.emit_json`, pytest, basedpyright, ruff.
---
## File Structure
- Create: `src/wf_cli/commands/status.py`
- Owns `wf status`.
- Builds one compact JSON payload from existing CLI context surfaces.
- Contains small private helpers for safe optional calls.
- Modify: `src/wf_cli/app.py`
- Register `status` as a top-level command.
- Modify: `tests/wf_cli/test_remote_target.py`
- Add remote status test using the existing ASGI-backed RPC client patch seam.
- Create: `tests/wf_cli/test_status.py`
- Add local/static and partial-unavailable behavior tests.
- Modify: `docs/wf_cli.md`
- Document `wf status`.
- Modify: `docs/current_roadmap.md`
- Mark this slice complete after implementation.
Do not modify `wf_server`, `wf_api`, or `wf_transport_rpc_http` unless tests prove an existing surface is missing.
---
## Payload Contract
`wf status` must emit JSON:
```json
{
"target": {
"mode": "local|remote",
"config_path": "wf.config.json",
"url": "http://127.0.0.1:8765/rpc|null"
},
"workflow": {
"capability_count": 88,
"sample_capabilities": ["wf.std.constant"]
},
"sources": {
"available": true,
"source_count": 7,
"sample_sources": ["wf.std"]
},
"admin": {
"available": true,
"connection_count": 4,
"status_count": 4,
"event_count": 12,
"auth_count": 1
},
"registry": {
"available": true,
"entry_count": 0
}
}
```
Rules:
- Counts may be `0`.
- Optional sections must keep `available: false` when unavailable.
- Do not include auth payload values.
- Do not include full capability/source/connection lists; this command is a quick orientation summary.
- Do not mutate config, stores, registry, auth, or catalog.
---
### Task 1: Add Local Status Command Tests
**Files:**
- Create: `tests/wf_cli/test_status.py`
- Create later: `src/wf_cli/commands/status.py`
- Modify later: `src/wf_cli/app.py`
- [ ] **Step 1: Write failing local status test**
Create `tests/wf_cli/test_status.py`:
```python
from __future__ import annotations
import json
from typer.testing import CliRunner
from wf_cli.app import app
def test_wf_status_local_static_target(tmp_path) -> None:
config_path = tmp_path / "wf.json"
config_path.write_text(
json.dumps(
{
"version": 1,
"client": {"target": {"kind": "local"}},
"server": {
"store": {
"kind": "filesystem",
"root": str(tmp_path / "store"),
}
},
}
),
encoding="utf-8",
)
result = CliRunner().invoke(
app,
[
"--config",
str(config_path),
"--local",
"status",
],
)
assert result.exit_code == 0, result.output
payload = json.loads(result.output)
assert payload["target"]["mode"] == "local"
assert payload["target"]["config_path"] == str(config_path)
assert payload["target"]["url"] is None
assert payload["workflow"]["capability_count"] >= 1
assert "wf.std.constant" in payload["workflow"]["sample_capabilities"]
assert payload["sources"]["available"] is True
assert payload["sources"]["source_count"] >= 1
assert payload["admin"]["available"] is True
assert payload["registry"]["available"] is False
```
- [ ] **Step 2: Run the failing test**
Run:
```powershell
uv run pytest tests/wf_cli/test_status.py::test_wf_status_local_static_target -q
```
Expected: FAIL because `wf status` is not registered.
---
### Task 2: Implement `wf status`
**Files:**
- Create: `src/wf_cli/commands/status.py`
- Modify: `src/wf_cli/app.py`
- [ ] **Step 1: Create status command module**
Create `src/wf_cli/commands/status.py`:
```python
from __future__ import annotations
from typing import Any
import typer
from wf_cli.context import CliTyperState, load_cli_context_from_typer
from wf_cli.io import emit_json
from wf_cli.remote_errors import run_cli_operation
def status_command(ctx: typer.Context) -> None:
"""Print a compact read-only summary of the selected workflow target."""
state = CliTyperState.from_context(ctx)
context = load_cli_context_from_typer(ctx)
payload: dict[str, Any] = {
"target": {
"mode": "remote" if state.rpc_url is not None else "local",
"config_path": str(context.config_path),
"url": state.rpc_url,
},
"workflow": _workflow_status(context),
"sources": _sources_status(context),
"admin": _admin_status(context),
"registry": _registry_status(context),
}
emit_json(payload)
def _workflow_status(context) -> dict[str, Any]:
capabilities = run_cli_operation(
context,
context.handlers.list_capabilities(limit=20),
)
items = capabilities.get("capabilities", [])
names = [
item.get("name")
for item in items
if isinstance(item, dict) and isinstance(item.get("name"), str)
]
return {
"capability_count": len(items),
"sample_capabilities": names[:5],
}
def _sources_status(context) -> dict[str, Any]:
try:
payload = run_cli_operation(context, context.source_admin.list_sources(limit=20))
except Exception as exc:
return _unavailable(exc)
sources = payload.get("sources", [])
source_ids = [
item.get("id")
for item in sources
if isinstance(item, dict) and isinstance(item.get("id"), str)
]
return {
"available": True,
"source_count": len(sources),
"sample_sources": source_ids[:5],
}
def _admin_status(context) -> dict[str, Any]:
try:
connections = run_cli_operation(context, context.admin.list_connections())
statuses = run_cli_operation(context, context.admin.get_connection_statuses())
events = run_cli_operation(context, context.admin.list_events())
auth = run_cli_operation(context, context.admin.list_auth_records())
except Exception as exc:
return _unavailable(exc)
return {
"available": True,
"connection_count": len(connections.get("connections", [])),
"status_count": len(statuses.get("statuses", [])),
"event_count": len(events.get("events", [])),
"auth_count": len(auth.get("auth_records", [])),
}
def _registry_status(context) -> dict[str, Any]:
admin = context.source_registry_admin
if admin is None:
return {"available": False, "reason": "source registry admin is not configured"}
try:
payload = run_cli_operation(context, admin.list_registry_entries(limit=20))
except Exception as exc:
return _unavailable(exc)
return {
"available": True,
"entry_count": len(payload.get("entries", [])),
}
def _unavailable(exc: Exception) -> dict[str, Any]:
return {
"available": False,
"reason": str(exc),
}
```
- [ ] **Step 2: Register top-level command**
Modify `src/wf_cli/app.py`:
```python
from .commands import (
admin,
artifacts,
caps,
deployments,
docs,
drafts,
explain,
runs,
schema,
sources,
status,
)
```
Add registration near the other top-level commands:
```python
app.command("status")(status.status_command)
```
- [ ] **Step 3: Run local status test**
Run:
```powershell
uv run pytest tests/wf_cli/test_status.py::test_wf_status_local_static_target -q
```
Expected: PASS.
- [ ] **Step 4: Typecheck the new command**
If basedpyright reports unknown types in helper functions, add local annotations by importing `CliContext`:
```python
from wf_cli.context import CliContext, CliTyperState, load_cli_context_from_typer
```
Then annotate helpers:
```python
def _workflow_status(context: CliContext) -> dict[str, Any]:
```
Run:
```powershell
uv run basedpyright --level error src\wf_cli\commands\status.py tests\wf_cli\test_status.py
```
Expected: `0 errors`.
---
### Task 3: Add Remote Status Test
**Files:**
- Modify: `tests/wf_cli/test_remote_target.py`
- [ ] **Step 1: Add remote test using existing RPC patch helper**
Append this test near the existing remote target CLI tests:
```python
def test_wf_status_uses_rpc_url_override(monkeypatch, tmp_path) -> None:
server = build_local_static_workflow_server(tmp_path / "store")
_patch_rpc_client_to_server(monkeypatch, server)
config_path = tmp_path / "wf.json"
config_path.write_text('{"version": 1}', encoding="utf-8")
result = CliRunner().invoke(
app,
[
"--config",
str(config_path),
"--url",
"http://test/rpc",
"status",
],
)
assert result.exit_code == 0, result.output
payload = json.loads(result.output)
assert payload["target"]["mode"] == "remote"
assert payload["target"]["url"] == "http://test/rpc"
assert payload["workflow"]["capability_count"] >= 1
assert payload["sources"]["available"] is True
assert payload["admin"]["available"] is True
assert payload["registry"]["available"] is False
```
- [ ] **Step 2: Run remote test**
Run:
```powershell
uv run pytest tests/wf_cli/test_remote_target.py::test_wf_status_uses_rpc_url_override -q
```
Expected: PASS.
---
### Task 4: Add Product Smoke Script Documentation
**Files:**
- Modify: `docs/wf_cli.md`
- Modify: `docs/current_roadmap.md`
- [ ] **Step 1: Document `wf status`**
In `docs/wf_cli.md`, under the remote server section or before capability
discovery, add:
````markdown
Check the selected target:
```bash
wf status
wf --url http://127.0.0.1:8765/rpc status
```
`status` is read-only. It reports the selected target, capability/source
availability, admin counts, auth record count, and desired registry count when
the target exposes those admin surfaces. It does not return auth payload values.
```
````
- [ ] **Step 2: Mark roadmap item complete**
In `docs/current_roadmap.md`, under `Priority 1: Product Smoke And Status UX`,
change:
```markdown
- Add `wf status` as a compact target/server status command.
```
to:
```markdown
- Completed: `wf status` is a compact read-only target/server status command.
```
Leave the manual product smoke item open.
- [ ] **Step 3: Run docs lint**
Run:
```powershell
uv run ruff check docs\wf_cli.md docs\current_roadmap.md
```
Expected: no lint errors. Ruff may report "No Python files found"; that is acceptable for markdown-only paths.
---
### Task 5: Final Verification
**Files:** all touched files.
- [ ] **Step 1: Run focused tests**
Run:
```powershell
uv run pytest tests/wf_cli/test_status.py tests/wf_cli/test_remote_target.py -q
```
Expected: all tests pass.
- [ ] **Step 2: Run broader CLI/RPC smoke tests**
Run:
```powershell
uv run pytest tests/wf_transport_rpc_http tests/wf_cli/test_remote_target.py tests/wf_cli/test_status.py -q
```
Expected: all tests pass.
- [ ] **Step 3: Run lint**
Run:
```powershell
uv run ruff check src\wf_cli tests\wf_cli tests\wf_transport_rpc_http
```
Expected: `All checks passed!`
- [ ] **Step 4: Run typecheck**
Run:
```powershell
uv run basedpyright --level error src\wf_cli tests\wf_cli\test_status.py tests\wf_cli\test_remote_target.py
```
Expected: `0 errors, 0 warnings, 0 notes`.
- [ ] **Step 5: Manual product smoke**
If a server is running:
```powershell
uv run wf --url http://127.0.0.1:8765/rpc status
uv run wf --url http://127.0.0.1:8765/rpc cap call wf.std.constant --input '{"value":"status smoke"}'
```
Expected:
- `status` returns JSON with `target.mode == "remote"`.
- `cap call` returns `outcome == "ok"`.
- [ ] **Step 6: Commit**
```powershell
git add src\wf_cli\commands\status.py src\wf_cli\app.py tests\wf_cli\test_status.py tests\wf_cli\test_remote_target.py docs\wf_cli.md docs\current_roadmap.md
git commit -m "feat: add workflow status command"
```
---
## Self-Review Checklist
- Spec coverage: this plan implements the roadmap's first Priority 1 item (`wf status`) and leaves manual product smoke as a visible follow-up.
- Placeholder scan: no TODO/TBD placeholders remain.
- Scope check: no new backend endpoint, no store mutation, no new server state.
- Type consistency: `CliContext`, `CliTyperState`, and existing admin/source methods match current code.
- Security: auth status returns counts only; no payload values.