# Auth Admin Mutation Slice 4 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 local/dev auth record save and delete through neutral admin, JSON-RPC, and `wf admin auth` CLI without exposing secret payload values in responses. **Architecture:** Extend the existing neutral `WorkflowAdminApi` auth surface with mutation methods, then implement those methods in the MCP-backed file-store provider. JSON-RPC and CLI call the neutral admin surface; do not add new `wf-mcp` tools or old broker-only product behavior. This is a local/dev file-store slice only: no OAuth, no production secret manager, no provider-specific auth variants, and no secret payload values in response bodies. **Tech Stack:** Python 3.14, dataclasses, Pydantic v2, Typer, JSON-RPC HTTP transport, pytest, ruff, basedpyright. --- ## Boundaries - Do not add new behavior to the legacy `wf-mcp` script. - Do not expose auth payload values in list, inspect, save, or delete responses. - Do not support inline auth records in `wf_config` or source registry documents. - Do not add OAuth/browser login, encryption, or secret-manager integration. - Do not change the on-disk auth JSON shape in this slice. - Do use `wf_api.auth.AuthRecord` as the neutral input shape for save/upsert. - Do keep the current compatibility `wf_mcp.auth.AuthRecord(connection_id, scheme, payload)` file adapter. ## Files - Modify `src/wf_api/admin.py` - Add mutation methods to `WorkflowAdminAuthProvider`. - Add `WorkflowAdminApi.save_auth_record(...)`. - Add `WorkflowAdminApi.delete_auth_record(...)`. - Modify `src/wf_api/surface.py` - Add mutation methods to `WorkflowAdminSurface`. - Modify `src/wf_mcp/storage/store.py` - Add `delete_auth(...)` and `delete_auth_record(...)` to `Store` / `FileStore`. - Modify `src/wf_mcp/broker/service/auth_admin.py` - Implement save/delete using `Store.save_auth_record` and `Store.delete_auth_record`. - Modify `src/wf_transport_rpc_http/models.py` - Add `SaveAuthParams` and `DeleteAuthParams`. - Modify `src/wf_transport_rpc_http/methods_admin.py` - Register `workflow.admin.auth.save` and `workflow.admin.auth.delete`. - Modify `src/wf_transport_rpc_http/client_admin.py` - Add `save_auth_record(...)` and `delete_auth_record(...)`. - Modify `src/wf_cli/commands/auth_admin.py` - Add `wf admin auth save`. - Add `wf admin auth delete --confirm`. - Modify docs: - `docs/superpowers/specs/2026-06-06-auth-source-secrets-boundary.md` - `docs/current_roadmap.md` - `docs/wf_cli.md` - Add/update tests: - `tests/wf_api/test_admin_api.py` - `tests/wf_mcp/service/test_auth_admin.py` - `tests/wf_mcp/test_auth.py` or `tests/wf_mcp/test_store.py` - `tests/wf_transport_rpc_http/test_admin_auth_rpc.py` - `tests/wf_cli/test_auth_admin.py` --- ### Task 1: Neutral Admin Auth Mutation Surface **Files:** - Modify: `src/wf_api/admin.py` - Modify: `src/wf_api/surface.py` - Test: `tests/wf_api/test_admin_api.py` - [ ] **Step 1: Add failing neutral API tests** Append these tests to `tests/wf_api/test_admin_api.py`. If a local fake provider already exists in the file, extend it instead of duplicating the whole class. ```python from wf_api.auth import AuthRecord class MutableAuthProvider(AuthProvider): def __init__(self) -> None: self.records: dict[str, dict[str, Any]] = {} def list_auth_records(self): return list(self.records.values()) def inspect_auth_record(self, auth_ref: str): try: return self.records[auth_ref] except KeyError as exc: raise KeyError(auth_ref) from exc def save_auth_record(self, record: AuthRecord): self.records[record.id] = { "id": record.id, "scheme": record.scheme, "metadata": dict(record.metadata), "payload_keys": sorted(str(key) for key in record.payload), } return self.records[record.id] def delete_auth_record(self, auth_ref: str): if auth_ref not in self.records: raise KeyError(auth_ref) del self.records[auth_ref] return {"deleted": True, "id": auth_ref} def test_admin_saves_auth_record_without_payload_values() -> None: provider = MutableAuthProvider() api = _api(provider) payload = asyncio.run( api.save_auth_record( auth_ref="drive.work", scheme="bearer", payload={"token": "secret"}, metadata={"owner": "test"}, ) ) assert payload == { "id": "drive.work", "scheme": "bearer", "metadata": {"owner": "test"}, "payload_keys": ["token"], } assert "secret" not in str(payload) def test_admin_deletes_auth_record() -> None: provider = MutableAuthProvider() api = _api(provider) asyncio.run( api.save_auth_record( auth_ref="drive.work", scheme="bearer", payload={"token": "secret"}, ) ) payload = asyncio.run(api.delete_auth_record("drive.work")) assert payload == {"deleted": True, "id": "drive.work"} with pytest.raises(KeyError): provider.inspect_auth_record("drive.work") def test_admin_auth_mutations_report_unavailable_without_provider() -> None: with pytest.raises(RuntimeError, match="auth admin is not available"): asyncio.run( _api().save_auth_record( auth_ref="drive.work", scheme="bearer", payload={"token": "secret"}, ) ) with pytest.raises(RuntimeError, match="auth admin is not available"): asyncio.run(_api().delete_auth_record("drive.work")) ``` - [ ] **Step 2: Run the failing tests** Run: ```powershell uv run pytest tests\wf_api\test_admin_api.py -q ``` Expected: fail because `WorkflowAdminApi.save_auth_record` and `delete_auth_record` do not exist. - [ ] **Step 3: Implement neutral API methods** In `src/wf_api/admin.py`, add: ```python from wf_api.auth import AuthRecord ``` Extend `WorkflowAdminAuthProvider`: ```python class WorkflowAdminAuthProvider(Protocol): """Provides auth inventory and local/dev auth mutation.""" def list_auth_records(self) -> Sequence[Mapping[str, Any] | object]: ... def inspect_auth_record(self, auth_ref: str) -> Mapping[str, Any] | object: ... def save_auth_record(self, record: AuthRecord) -> Mapping[str, Any] | object: ... def delete_auth_record(self, auth_ref: str) -> Mapping[str, Any] | object: ... ``` Add methods to `WorkflowAdminApi`: ```python async def save_auth_record( self, *, auth_ref: str, scheme: str, payload: Mapping[str, object], metadata: Mapping[str, object] | None = None, ) -> dict[str, Any]: if self.auth is None: raise RuntimeError("auth admin is not available for this target") record = AuthRecord( id=auth_ref, scheme=scheme, payload=dict(payload), metadata=dict(metadata or {}), ) return _payload(self.auth.save_auth_record(record)) async def delete_auth_record(self, auth_ref: str) -> dict[str, Any]: if self.auth is None: raise RuntimeError("auth admin is not available for this target") return _payload(self.auth.delete_auth_record(auth_ref)) ``` In `src/wf_api/surface.py`, add these methods to `WorkflowAdminSurface`: ```python async def save_auth_record( self, *, auth_ref: str, scheme: str, payload: Mapping[str, object], metadata: Mapping[str, object] | None = None, ) -> dict[str, Any]: ... async def delete_auth_record(self, auth_ref: str) -> dict[str, Any]: ... ``` If `Mapping` is not imported in `surface.py`, import it from `collections.abc`. - [ ] **Step 4: Run tests** Run: ```powershell uv run pytest tests\wf_api\test_admin_api.py -q uv run ruff check src\wf_api\admin.py src\wf_api\surface.py tests\wf_api\test_admin_api.py uv run basedpyright --level error src\wf_api\admin.py src\wf_api\surface.py tests\wf_api\test_admin_api.py ``` Expected: pass. --- ### Task 2: MCP File Store Save/Delete Provider **Files:** - Modify: `src/wf_mcp/storage/store.py` - Modify: `src/wf_mcp/broker/service/auth_admin.py` - Test: `tests/wf_mcp/test_store.py` - Test: `tests/wf_mcp/service/test_auth_admin.py` - [ ] **Step 1: Add failing store tests** Append to `tests/wf_mcp/test_store.py`: ```python def test_file_store_deletes_auth_record(tmp_path) -> None: store = FileStore(tmp_path / "store") record = AuthRecord( connection_id="drive.work", scheme="bearer", payload={"token": "secret"}, ) store.save_auth(record) assert store.load_auth("drive.work") == record assert store.delete_auth("drive.work") is True assert store.load_auth("drive.work") is None assert store.delete_auth("drive.work") is False def test_file_store_deletes_neutral_auth_record(tmp_path) -> None: store = FileStore(tmp_path / "store") store.save_auth_record( NeutralAuthRecord( id="drive.work", scheme="bearer", payload={"token": "secret"}, ) ) assert store.delete_auth_record("drive.work") is True assert store.load_auth_record("drive.work") is None ``` Add imports if needed: ```python from wf_api.auth import AuthRecord as NeutralAuthRecord ``` - [ ] **Step 2: Add failing provider tests** Append to `tests/wf_mcp/service/test_auth_admin.py`: ```python from wf_api.auth import AuthRecord as NeutralAuthRecord def test_auth_admin_provider_saves_auth_without_returning_payload(tmp_path) -> None: store = FileStore(tmp_path / "store") provider = McpAuthAdminProvider(store) payload = provider.save_auth_record( NeutralAuthRecord( id="drive.work", scheme="bearer", payload={"token": "secret"}, metadata={"owner": "test"}, ) ) assert payload == { "id": "drive.work", "scheme": "bearer", "metadata": {}, "payload_keys": ["token"], } assert "secret" not in str(payload) assert store.load_auth("drive.work") == AuthRecord( connection_id="drive.work", scheme="bearer", payload={"token": "secret"}, ) def test_auth_admin_provider_deletes_auth(tmp_path) -> None: store = FileStore(tmp_path / "store") provider = McpAuthAdminProvider(store) store.save_auth(AuthRecord(connection_id="drive.work", scheme="bearer")) payload = provider.delete_auth_record("drive.work") assert payload == {"deleted": True, "id": "drive.work"} assert store.load_auth("drive.work") is None def test_auth_admin_provider_delete_unknown_auth_raises_key_error(tmp_path) -> None: provider = McpAuthAdminProvider(FileStore(tmp_path / "store")) with pytest.raises(KeyError, match="unknown auth record 'missing'"): provider.delete_auth_record("missing") ``` - [ ] **Step 3: Run failing tests** Run: ```powershell uv run pytest tests\wf_mcp\test_store.py tests\wf_mcp\service\test_auth_admin.py -q ``` Expected: fail because delete/save provider mutation methods are missing. - [ ] **Step 4: Implement store delete methods** In `src/wf_mcp/storage/store.py`, extend `Store`: ```python def delete_auth(self, connection_id: str) -> bool: raise NotImplementedError def delete_auth_record(self, auth_ref: str) -> bool: raise NotImplementedError ``` In `FileStore`, add: ```python def delete_auth(self, connection_id: str) -> bool: path = self._auth_path(connection_id) if not path.exists(): return False path.unlink() return True def delete_auth_record(self, auth_ref: str) -> bool: """Delete neutral auth through the legacy MCP file shape.""" return self.delete_auth(auth_ref) ``` - [ ] **Step 5: Implement provider methods** In `src/wf_mcp/broker/service/auth_admin.py`, import neutral auth: ```python from wf_api.auth import AuthRecord as NeutralAuthRecord ``` Add methods to `McpAuthAdminProvider`: ```python def save_auth_record(self, record: NeutralAuthRecord) -> dict[str, Any]: self.store.save_auth_record(record) return self.inspect_auth_record(record.id) def delete_auth_record(self, auth_ref: str) -> dict[str, Any]: deleted = self.store.delete_auth_record(auth_ref) if not deleted: raise KeyError(f"unknown auth record {auth_ref!r}") return {"deleted": True, "id": auth_ref} ``` - [ ] **Step 6: Run tests** Run: ```powershell uv run pytest tests\wf_mcp\test_store.py tests\wf_mcp\service\test_auth_admin.py -q uv run ruff check src\wf_mcp\storage\store.py src\wf_mcp\broker\service\auth_admin.py tests\wf_mcp\test_store.py tests\wf_mcp\service\test_auth_admin.py uv run basedpyright --level error src\wf_mcp\storage\store.py src\wf_mcp\broker\service\auth_admin.py tests\wf_mcp\test_store.py tests\wf_mcp\service\test_auth_admin.py ``` Expected: pass. --- ### Task 3: JSON-RPC Auth Mutation Methods **Files:** - Modify: `src/wf_transport_rpc_http/models.py` - Modify: `src/wf_transport_rpc_http/methods_admin.py` - Modify: `src/wf_transport_rpc_http/client_admin.py` - Test: `tests/wf_transport_rpc_http/test_admin_auth_rpc.py` - [ ] **Step 1: Add failing RPC tests** Append to `tests/wf_transport_rpc_http/test_admin_auth_rpc.py`: ```python async def test_rpc_saves_auth_record_without_returning_payload(tmp_path) -> None: store = FileStore(tmp_path / "store") server = build_workflow_server_from_config(BrokerConfig(store_root=store.root)) async with _client(server) as client: payload = await client.save_auth_record( auth_ref="drive.work", scheme="bearer", payload={"token": "secret"}, metadata={"owner": "test"}, ) assert payload["id"] == "drive.work" assert payload["scheme"] == "bearer" assert payload["payload_keys"] == ["token"] assert "secret" not in str(payload) assert FileStore(store.root).load_auth("drive.work") == AuthRecord( connection_id="drive.work", scheme="bearer", payload={"token": "secret"}, ) async def test_rpc_deletes_auth_record(tmp_path) -> None: store = FileStore(tmp_path / "store") store.save_auth(AuthRecord(connection_id="drive.work", scheme="bearer")) server = build_workflow_server_from_config(BrokerConfig(store_root=store.root)) async with _client(server) as client: payload = await client.delete_auth_record("drive.work") assert payload == {"deleted": True, "id": "drive.work"} assert FileStore(store.root).load_auth("drive.work") is None ``` Use the existing `_client(...)` helper in this test file. If it has a different name, adapt only the helper call. - [ ] **Step 2: Run failing tests** Run: ```powershell uv run pytest tests\wf_transport_rpc_http\test_admin_auth_rpc.py -q ``` Expected: fail because RPC client/server mutation methods do not exist. - [ ] **Step 3: Add RPC parameter models** In `src/wf_transport_rpc_http/models.py`, add: ```python class SaveAuthParams(RpcParamsModel): auth_ref: str = Field(min_length=1) scheme: str = Field(min_length=1) payload: dict[str, Any] = Field(default_factory=dict) metadata: dict[str, Any] = Field(default_factory=dict) class DeleteAuthParams(RpcParamsModel): auth_ref: str = Field(min_length=1) ``` If `Any` is not imported in that file, import it from `typing`. - [ ] **Step 4: Register RPC methods** In `src/wf_transport_rpc_http/methods_admin.py`, import the new params: ```python from .models import AdminEmptyParams, DeleteAuthParams, InspectAuthParams, SaveAuthParams ``` Add methods after `workflow.admin.auth.inspect`: ```python @entrypoint.method( name="workflow.admin.auth.save", errors=[WorkflowRpcError], ) async def workflow_admin_auth_save( params: SaveAuthParams = RpcParams(), ) -> dict[str, Any]: try: return await server.admin.save_auth_record( auth_ref=params.auth_ref, scheme=params.scheme, payload=params.payload, metadata=params.metadata, ) except ( ValueError, KeyError, LookupError, FileNotFoundError, RuntimeError, ) as exc: raise_workflow_rpc_error(exc) @entrypoint.method( name="workflow.admin.auth.delete", errors=[WorkflowRpcError], ) async def workflow_admin_auth_delete( params: DeleteAuthParams = RpcParams(), ) -> dict[str, Any]: try: return await server.admin.delete_auth_record(params.auth_ref) except ( ValueError, KeyError, LookupError, FileNotFoundError, RuntimeError, ) as exc: raise_workflow_rpc_error(exc) ``` - [ ] **Step 5: Add RPC client methods** In `src/wf_transport_rpc_http/client_admin.py`, add: ```python async def save_auth_record( self, *, auth_ref: str, scheme: str, payload: dict[str, Any], metadata: dict[str, Any] | None = None, ) -> dict[str, Any]: return await self._call( "workflow.admin.auth.save", { "auth_ref": auth_ref, "scheme": scheme, "payload": payload, "metadata": metadata or {}, }, ) async def delete_auth_record(self, auth_ref: str) -> dict[str, Any]: return await self._call( "workflow.admin.auth.delete", {"auth_ref": auth_ref}, ) ``` - [ ] **Step 6: Run tests** Run: ```powershell uv run pytest tests\wf_transport_rpc_http\test_admin_auth_rpc.py -q uv run ruff check src\wf_transport_rpc_http\models.py src\wf_transport_rpc_http\methods_admin.py src\wf_transport_rpc_http\client_admin.py tests\wf_transport_rpc_http\test_admin_auth_rpc.py uv run basedpyright --level error src\wf_transport_rpc_http\models.py src\wf_transport_rpc_http\methods_admin.py src\wf_transport_rpc_http\client_admin.py tests\wf_transport_rpc_http\test_admin_auth_rpc.py ``` Expected: pass. --- ### Task 4: CLI Auth Save/Delete **Files:** - Modify: `src/wf_cli/commands/auth_admin.py` - Test: `tests/wf_cli/test_auth_admin.py` - [ ] **Step 1: Add failing CLI tests** Append to `tests/wf_cli/test_auth_admin.py`: ```python def test_wf_admin_auth_save(monkeypatch) -> None: mock_admin = MockAuthAdmin() monkeypatch.setattr( "wf_cli.commands.auth_admin.load_cli_context_from_typer", lambda _ctx: SimpleNamespace(admin=mock_admin), ) result = CliRunner().invoke( app, [ "admin", "auth", "save", "drive.work", "--scheme", "bearer", "--payload", '{"token":"secret"}', ], ) assert result.exit_code == 0 payload = json.loads(result.output) assert payload["id"] == "drive.work" assert payload["payload_keys"] == ["token"] assert "secret" not in result.output mock_admin.save_auth_record.assert_called_once_with( auth_ref="drive.work", scheme="bearer", payload={"token": "secret"}, metadata=None, ) def test_wf_admin_auth_save_reads_payload_file(tmp_path, monkeypatch) -> None: mock_admin = MockAuthAdmin() payload_file = tmp_path / "auth.json" payload_file.write_text('{"token":"secret"}', encoding="utf-8") monkeypatch.setattr( "wf_cli.commands.auth_admin.load_cli_context_from_typer", lambda _ctx: SimpleNamespace(admin=mock_admin), ) result = CliRunner().invoke( app, [ "admin", "auth", "save", "drive.work", "--scheme", "bearer", "--payload-file", str(payload_file), ], ) assert result.exit_code == 0 mock_admin.save_auth_record.assert_called_once_with( auth_ref="drive.work", scheme="bearer", payload={"token": "secret"}, metadata=None, ) def test_wf_admin_auth_delete_requires_confirm(monkeypatch) -> None: mock_admin = MockAuthAdmin() monkeypatch.setattr( "wf_cli.commands.auth_admin.load_cli_context_from_typer", lambda _ctx: SimpleNamespace(admin=mock_admin), ) result = CliRunner().invoke(app, ["admin", "auth", "delete", "drive.work"]) assert result.exit_code != 0 assert "--confirm" in result.output mock_admin.delete_auth_record.assert_not_called() def test_wf_admin_auth_delete(monkeypatch) -> None: mock_admin = MockAuthAdmin() monkeypatch.setattr( "wf_cli.commands.auth_admin.load_cli_context_from_typer", lambda _ctx: SimpleNamespace(admin=mock_admin), ) result = CliRunner().invoke( app, ["admin", "auth", "delete", "drive.work", "--confirm"], ) assert result.exit_code == 0 assert json.loads(result.output) == {"deleted": True, "id": "drive.work"} mock_admin.delete_auth_record.assert_called_once_with("drive.work") ``` If the test file uses a different fake than `MockAuthAdmin`, extend the existing fake with `save_auth_record = MagicMock(...)` and `delete_auth_record = MagicMock(...)`. - [ ] **Step 2: Run failing CLI tests** Run: ```powershell uv run pytest tests\wf_cli\test_auth_admin.py -q ``` Expected: fail because commands are missing. - [ ] **Step 3: Implement JSON input helper locally** In `src/wf_cli/commands/auth_admin.py`, add imports: ```python import json from pathlib import Path ``` Add helpers: ```python def _read_json_object( inline: str | None, file_path: str | None, flag_names: str, ) -> dict[str, object]: if inline and file_path: raise typer.BadParameter(f"provide exactly one of {flag_names}") if inline: try: value = json.loads(inline) except json.JSONDecodeError as exc: raise typer.BadParameter(f"invalid JSON: {exc}") from exc if not isinstance(value, dict): raise typer.BadParameter(f"{flag_names} must be a JSON object") return dict(value) if file_path: try: value = json.loads(Path(file_path).read_text(encoding="utf-8")) except FileNotFoundError as exc: raise typer.BadParameter(f"file not found: {file_path}") from exc except json.JSONDecodeError as exc: raise typer.BadParameter(f"invalid JSON in file: {exc}") from exc if not isinstance(value, dict): raise typer.BadParameter(f"{flag_names} must be a JSON object") return dict(value) raise typer.BadParameter(f"{flag_names} is required") ``` - [ ] **Step 4: Add CLI commands** In `src/wf_cli/commands/auth_admin.py`, add: ```python @app.command("save") def save_auth_record( ctx: typer.Context, auth_ref: Annotated[str, typer.Argument(help="Auth record id/ref.")], scheme: Annotated[str, typer.Option("--scheme", help="Auth scheme/kind.")], payload_json: Annotated[ str | None, typer.Option("--payload", help="Secret payload JSON object."), ] = None, payload_file: Annotated[ str | None, typer.Option("--payload-file", help="File containing secret payload JSON object."), ] = None, metadata_json: Annotated[ str | None, typer.Option("--metadata", help="Non-secret metadata JSON object."), ] = None, metadata_file: Annotated[ str | None, typer.Option("--metadata-file", help="File containing non-secret metadata JSON object."), ] = None, ) -> None: """Save or replace a local/dev auth record; response never includes payload values.""" payload = _read_json_object(payload_json, payload_file, "--payload/--payload-file") metadata = ( _read_json_object(metadata_json, metadata_file, "--metadata/--metadata-file") if metadata_json or metadata_file else None ) context = load_cli_context_from_typer(ctx) result = run_cli_operation( context, context.admin.save_auth_record( auth_ref=auth_ref, scheme=scheme, payload=payload, metadata=metadata, ), ) emit_json(result) @app.command("delete") def delete_auth_record( ctx: typer.Context, auth_ref: Annotated[str, typer.Argument(help="Auth record id/ref.")], confirm: Annotated[ bool, typer.Option("--confirm", help="Required to delete an auth record."), ] = False, ) -> None: """Delete a local/dev auth record.""" if not confirm: raise typer.BadParameter("--confirm is required to delete an auth record") context = load_cli_context_from_typer(ctx) result = run_cli_operation(context, context.admin.delete_auth_record(auth_ref)) emit_json(result) ``` - [ ] **Step 5: Run CLI tests** Run: ```powershell uv run pytest tests\wf_cli\test_auth_admin.py -q uv run ruff check src\wf_cli\commands\auth_admin.py tests\wf_cli\test_auth_admin.py uv run basedpyright --level error src\wf_cli\commands\auth_admin.py tests\wf_cli\test_auth_admin.py ``` Expected: pass. --- ### Task 5: Docs and Final Verification **Files:** - Modify: `docs/superpowers/specs/2026-06-06-auth-source-secrets-boundary.md` - Modify: `docs/current_roadmap.md` - Modify: `docs/wf_cli.md` - [ ] **Step 1: Update auth spec status** In `docs/superpowers/specs/2026-06-06-auth-source-secrets-boundary.md`, update the `## Status` paragraph to include: ```markdown Slice 4 adds local/dev file-backed auth save/delete through neutral admin, JSON-RPC, and CLI. Responses still expose only ids, schemes, metadata, and payload keys; secret payload values remain write-only. OAuth, production secret managers, and provider-specific auth variants remain future work. ``` - [ ] **Step 2: Update roadmap** In `docs/current_roadmap.md`, under the auth/source secrets boundary bullet, add: ```markdown Fourth implementation slice complete: local/dev auth records can be saved and deleted through neutral admin, JSON-RPC, and `wf admin auth`. This is still not a production secret manager or OAuth flow; payload values are accepted only as write inputs and never returned. ``` - [ ] **Step 3: Update CLI docs** In `docs/wf_cli.md`, add an auth section near admin/source registry commands: ```markdown ### Local/dev auth records Auth payload values are write-only. `list`, `inspect`, `save`, and `delete` responses show ids, schemes, metadata, and payload keys only. ```powershell wf admin auth save drive.work --scheme bearer --payload-file drive-auth.json wf admin auth list wf admin auth inspect drive.work wf admin auth delete drive.work --confirm ``` Use source `auth_ref` values to point sources at these records. Do not commit payload files containing real secrets. ``` ``` - [ ] **Step 4: Run final focused suite** Run: ```powershell uv run pytest tests\wf_api\test_admin_api.py tests\wf_mcp\test_store.py tests\wf_mcp\service\test_auth_admin.py tests\wf_transport_rpc_http\test_admin_auth_rpc.py tests\wf_cli\test_auth_admin.py -q uv run ruff check src\wf_api\admin.py src\wf_api\surface.py src\wf_mcp\storage\store.py src\wf_mcp\broker\service\auth_admin.py src\wf_transport_rpc_http\models.py src\wf_transport_rpc_http\methods_admin.py src\wf_transport_rpc_http\client_admin.py src\wf_cli\commands\auth_admin.py tests\wf_api\test_admin_api.py tests\wf_mcp\test_store.py tests\wf_mcp\service\test_auth_admin.py tests\wf_transport_rpc_http\test_admin_auth_rpc.py tests\wf_cli\test_auth_admin.py uv run basedpyright --level error src\wf_api src\wf_mcp\storage\store.py src\wf_mcp\broker\service\auth_admin.py src\wf_transport_rpc_http src\wf_cli\commands\auth_admin.py tests\wf_api\test_admin_api.py tests\wf_mcp\test_store.py tests\wf_mcp\service\test_auth_admin.py tests\wf_transport_rpc_http\test_admin_auth_rpc.py tests\wf_cli\test_auth_admin.py ``` Expected: pass. - [ ] **Step 5: Run broader smoke** Run: ```powershell uv run pytest tests\wf_mcp tests\wf_transport_rpc_http tests\wf_cli\test_auth_admin.py -q ``` Expected: pass with the existing skipped/xfail counts only. - [ ] **Step 6: Review security invariant** Run: ```powershell rg -n "\"payload\"|secret|token" src\wf_api\admin.py src\wf_mcp\broker\service\auth_admin.py src\wf_transport_rpc_http\methods_admin.py src\wf_cli\commands\auth_admin.py tests\wf_api\test_admin_api.py tests\wf_mcp\service\test_auth_admin.py tests\wf_transport_rpc_http\test_admin_auth_rpc.py tests\wf_cli\test_auth_admin.py ``` Expected: - Payload appears only as input construction or persisted store calls. - Response assertions use `payload_keys`. - Tests assert secret values are not present in output. --- ## Self-Review - Spec coverage: this plan implements local/dev auth save/delete, keeps read responses secret-free, and leaves OAuth/secret managers/provider unions future. - Placeholder scan: no TBD/TODO placeholders are present. - Type consistency: public API method names are `save_auth_record` and `delete_auth_record` across `wf_api`, JSON-RPC client, and CLI. RPC method names are `workflow.admin.auth.save` and `workflow.admin.auth.delete`. - Scope check: this plan does not touch legacy `wf-mcp` tools; it routes through neutral admin surfaces and JSON-RPC/CLI.