docs: plan mcp auth catalog store split
This commit is contained in:
@@ -0,0 +1,670 @@
|
||||
# Split MCP Auth And Catalog Stores 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:** Split the current MCP compatibility `FileStore` responsibilities so auth records and catalog/cache snapshots can use different configured roots.
|
||||
|
||||
**Architecture:** Keep `FileStore(root)` as the backwards-compatible combined store. Add focused `AuthStore` / `CatalogStore` protocols plus `FileAuthStore` / `FileCatalogStore` implementations. `WfMcpService` receives separate auth and catalog stores internally, while existing callers that pass only `store=FileStore(...)` keep working.
|
||||
|
||||
**Tech Stack:** Python dataclasses/protocol-style store classes, existing MCP broker services, pytest, ruff, basedpyright.
|
||||
|
||||
---
|
||||
|
||||
## File Map
|
||||
|
||||
- `src/wf_mcp/storage/store.py`: define focused store interfaces/classes and keep `FileStore` as a compatibility wrapper.
|
||||
- `src/wf_mcp/storage/__init__.py`: export new store types.
|
||||
- `src/wf_mcp/broker/service/core.py`: add optional `auth_store` and `catalog_store` fields; wire focused services to focused stores.
|
||||
- `src/wf_mcp/broker/service/upstream_transport.py`: accept `AuthStore` and `CatalogStore` instead of one combined `Store`.
|
||||
- `src/wf_mcp/broker/service/source_catalog.py`: accept `CatalogStore` instead of combined `Store`.
|
||||
- `src/wf_mcp/broker/config.py`: construct `FileAuthStore(auth_root)` and `FileCatalogStore(catalog_cache_root)` from `BrokerStoreRoots`.
|
||||
- `tests/wf_mcp/test_store.py`: focused store behavior tests.
|
||||
- `tests/wf_mcp/test_workflow_config_bridge.py`: prove auth and catalog role roots are both used.
|
||||
- `tests/wf_mcp/service/test_upstream_transport.py`, `tests/wf_mcp/service/test_catalog.py`: adjust direct service construction where needed.
|
||||
- Docs: `docs/current_roadmap.md`, `docs/superpowers/specs/2026-06-03-workflow-config-targets-and-sources.md`, `docs/wf_cli.md`.
|
||||
|
||||
---
|
||||
|
||||
## Hard Boundaries
|
||||
|
||||
- Do not remove `FileStore`; too many tests and compatibility paths still construct it.
|
||||
- Do not move this into `wf_api`; these stores are still MCP-shaped compatibility stores.
|
||||
- Do not add SQL/object/secret-manager stores.
|
||||
- Do not change on-disk JSON shapes:
|
||||
- auth remains `<auth-root>/auth/<auth_ref>.json`
|
||||
- catalog remains `<catalog-root>/catalog/<connection_id>.json`
|
||||
- Existing `WfMcpService(store=FileStore(root))` must keep using `root` for both auth and catalog.
|
||||
|
||||
---
|
||||
|
||||
## Task 1: Add Focused FileAuthStore And FileCatalogStore
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/wf_mcp/storage/store.py`
|
||||
- Modify: `src/wf_mcp/storage/__init__.py`
|
||||
- Test: `tests/wf_mcp/test_store.py`
|
||||
|
||||
- [ ] **Step 1: Add focused store tests**
|
||||
|
||||
Append to `tests/wf_mcp/test_store.py`:
|
||||
|
||||
```python
|
||||
def test_file_auth_store_uses_own_root(tmp_path) -> None:
|
||||
store = FileAuthStore(tmp_path / "auth_root")
|
||||
record = AuthRecord(
|
||||
connection_id="drive.work",
|
||||
scheme="bearer",
|
||||
payload={"token": "secret"},
|
||||
)
|
||||
|
||||
store.save_auth(record)
|
||||
|
||||
assert store.load_auth("drive.work") == record
|
||||
assert (tmp_path / "auth_root" / "auth" / "drive.work.json").exists()
|
||||
assert not (tmp_path / "auth_root" / "catalog").exists()
|
||||
|
||||
|
||||
def test_file_catalog_store_uses_own_root(tmp_path) -> None:
|
||||
store = FileCatalogStore(tmp_path / "catalog_root")
|
||||
snapshot = CatalogSnapshot(
|
||||
connection_id="drive.work",
|
||||
fetched_at_epoch_ms=1,
|
||||
max_age_seconds=300,
|
||||
nodes=[],
|
||||
resources=[],
|
||||
prompts=[],
|
||||
metadata={},
|
||||
)
|
||||
|
||||
store.save_catalog(snapshot)
|
||||
|
||||
assert store.load_catalog("drive.work") == snapshot
|
||||
assert (tmp_path / "catalog_root" / "catalog" / "drive.work.json").exists()
|
||||
assert not (tmp_path / "catalog_root" / "auth").exists()
|
||||
```
|
||||
|
||||
Add imports if missing:
|
||||
|
||||
```python
|
||||
from wf_mcp.storage import FileAuthStore, FileCatalogStore
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Run failing tests**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
uv run pytest tests/wf_mcp/test_store.py::test_file_auth_store_uses_own_root tests/wf_mcp/test_store.py::test_file_catalog_store_uses_own_root -q
|
||||
```
|
||||
|
||||
Expected: fail because `FileAuthStore` and `FileCatalogStore` do not exist.
|
||||
|
||||
- [ ] **Step 3: Split store interfaces and classes**
|
||||
|
||||
In `src/wf_mcp/storage/store.py`, replace the top-level `Store` class with:
|
||||
|
||||
```python
|
||||
class AuthStore:
|
||||
def save_auth(self, record: AuthRecord) -> None:
|
||||
raise NotImplementedError
|
||||
|
||||
def load_auth(self, connection_id: str) -> AuthRecord | None:
|
||||
raise NotImplementedError
|
||||
|
||||
def list_auth_refs(self) -> list[str]:
|
||||
raise NotImplementedError
|
||||
|
||||
def save_auth_record(self, record: NeutralAuthRecord) -> None:
|
||||
raise NotImplementedError
|
||||
|
||||
def load_auth_record(self, auth_ref: str) -> NeutralAuthRecord | None:
|
||||
raise NotImplementedError
|
||||
|
||||
def delete_auth(self, connection_id: str) -> bool:
|
||||
raise NotImplementedError
|
||||
|
||||
def delete_auth_record(self, auth_ref: str) -> bool:
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class CatalogStore:
|
||||
def save_catalog(self, snapshot: CatalogSnapshot) -> None:
|
||||
raise NotImplementedError
|
||||
|
||||
def load_catalog(self, connection_id: str) -> CatalogSnapshot | None:
|
||||
raise NotImplementedError
|
||||
|
||||
|
||||
class Store(AuthStore, CatalogStore):
|
||||
"""Compatibility store combining MCP auth and catalog/cache storage."""
|
||||
```
|
||||
|
||||
Then add `FileAuthStore` with the existing auth methods and `_auth_path` helper, and `FileCatalogStore` with the existing catalog methods, `_catalog_path`, and `_connection_path` helper.
|
||||
|
||||
Keep `FileStore` as:
|
||||
|
||||
```python
|
||||
class FileStore(Store):
|
||||
"""Compatibility file store that combines auth and catalog stores."""
|
||||
|
||||
def __init__(self, root: Path) -> None:
|
||||
self.root = root
|
||||
self._auth = FileAuthStore(root)
|
||||
self._catalog = FileCatalogStore(root)
|
||||
|
||||
@property
|
||||
def auth_dir(self) -> Path:
|
||||
return self._auth.auth_dir
|
||||
|
||||
@property
|
||||
def catalog_dir(self) -> Path:
|
||||
return self._catalog.catalog_dir
|
||||
|
||||
def save_auth(self, record: AuthRecord) -> None:
|
||||
self._auth.save_auth(record)
|
||||
|
||||
def load_auth(self, connection_id: str) -> AuthRecord | None:
|
||||
return self._auth.load_auth(connection_id)
|
||||
|
||||
def list_auth_refs(self) -> list[str]:
|
||||
return self._auth.list_auth_refs()
|
||||
|
||||
def save_auth_record(self, record: NeutralAuthRecord) -> None:
|
||||
self._auth.save_auth_record(record)
|
||||
|
||||
def load_auth_record(self, auth_ref: str) -> NeutralAuthRecord | None:
|
||||
return self._auth.load_auth_record(auth_ref)
|
||||
|
||||
def delete_auth(self, connection_id: str) -> bool:
|
||||
return self._auth.delete_auth(connection_id)
|
||||
|
||||
def delete_auth_record(self, auth_ref: str) -> bool:
|
||||
return self._auth.delete_auth_record(auth_ref)
|
||||
|
||||
def save_catalog(self, snapshot: CatalogSnapshot) -> None:
|
||||
self._catalog.save_catalog(snapshot)
|
||||
|
||||
def load_catalog(self, connection_id: str) -> CatalogSnapshot | None:
|
||||
return self._catalog.load_catalog(connection_id)
|
||||
```
|
||||
|
||||
Use docstrings/comments for the compatibility seam: future code should inject focused stores; `FileStore` remains for older callers.
|
||||
|
||||
- [ ] **Step 4: Export new stores**
|
||||
|
||||
In `src/wf_mcp/storage/__init__.py`, export:
|
||||
|
||||
```python
|
||||
AuthStore
|
||||
CatalogStore
|
||||
FileAuthStore
|
||||
FileCatalogStore
|
||||
```
|
||||
|
||||
Keep existing `Store` and `FileStore` exports.
|
||||
|
||||
- [ ] **Step 5: Verify store tests**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
uv run pytest tests/wf_mcp/test_store.py -q
|
||||
```
|
||||
|
||||
Expected: pass.
|
||||
|
||||
---
|
||||
|
||||
## Task 2: Split Store Dependencies In Broker Services
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/wf_mcp/broker/service/upstream_transport.py`
|
||||
- Modify: `src/wf_mcp/broker/service/source_catalog.py`
|
||||
- Modify: `src/wf_mcp/broker/service/core.py`
|
||||
- Test: `tests/wf_mcp/service/test_upstream_transport.py`
|
||||
- Test: `tests/wf_mcp/service/test_catalog.py`
|
||||
|
||||
- [ ] **Step 1: Add direct service tests for separate roots**
|
||||
|
||||
In `tests/wf_mcp/service/test_upstream_transport.py`, add:
|
||||
|
||||
```python
|
||||
def test_upstream_transport_uses_separate_auth_and_catalog_stores(tmp_path) -> None:
|
||||
auth_store = FileAuthStore(tmp_path / "auth")
|
||||
catalog_store = FileCatalogStore(tmp_path / "catalog")
|
||||
events = []
|
||||
transport = UpstreamTransportService(
|
||||
auth_store=auth_store,
|
||||
catalog_store=catalog_store,
|
||||
event_sink=events.append,
|
||||
)
|
||||
record = AuthRecord(connection_id="demo.personal", scheme="bearer")
|
||||
transport.save_auth(record)
|
||||
snapshot = CatalogSnapshot(
|
||||
connection_id="demo.personal",
|
||||
fetched_at_epoch_ms=1,
|
||||
max_age_seconds=300,
|
||||
nodes=[],
|
||||
resources=[],
|
||||
prompts=[],
|
||||
metadata={},
|
||||
)
|
||||
transport.catalog_store.save_catalog(snapshot)
|
||||
|
||||
assert (tmp_path / "auth" / "auth" / "demo.personal.json").exists()
|
||||
assert (tmp_path / "catalog" / "catalog" / "demo.personal.json").exists()
|
||||
```
|
||||
|
||||
In `tests/wf_mcp/service/test_catalog.py`, add:
|
||||
|
||||
```python
|
||||
def test_source_catalog_uses_catalog_store_only(tmp_path) -> None:
|
||||
catalog_store = FileCatalogStore(tmp_path / "catalog")
|
||||
service = SourceCatalogService(
|
||||
store=catalog_store,
|
||||
connection_lookup=lambda connection_id: ConnectionConfig(
|
||||
id=connection_id,
|
||||
server="demo",
|
||||
account="personal",
|
||||
),
|
||||
connection_list_enabled=lambda: [],
|
||||
connection_list_all=lambda: [],
|
||||
tool_executor_for=lambda connection: (_ for _ in ()).throw(
|
||||
AssertionError("unexpected executor")
|
||||
),
|
||||
load_auth=lambda connection: None,
|
||||
emit_event=lambda event: None,
|
||||
)
|
||||
snapshot = CatalogSnapshot(
|
||||
connection_id="demo.personal",
|
||||
fetched_at_epoch_ms=1,
|
||||
max_age_seconds=300,
|
||||
nodes=[],
|
||||
resources=[],
|
||||
prompts=[],
|
||||
metadata={},
|
||||
)
|
||||
service.store.save_catalog(snapshot)
|
||||
|
||||
assert service.store.load_catalog("demo.personal") == snapshot
|
||||
assert (tmp_path / "catalog" / "catalog" / "demo.personal.json").exists()
|
||||
```
|
||||
|
||||
Adjust imports to match existing test file conventions.
|
||||
|
||||
- [ ] **Step 2: Run failing tests**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
uv run pytest tests/wf_mcp/service/test_upstream_transport.py::test_upstream_transport_uses_separate_auth_and_catalog_stores tests/wf_mcp/service/test_catalog.py::test_source_catalog_uses_catalog_store_only -q
|
||||
```
|
||||
|
||||
Expected: fail because constructors still take one combined `store`.
|
||||
|
||||
- [ ] **Step 3: Update `UpstreamTransportService`**
|
||||
|
||||
In `src/wf_mcp/broker/service/upstream_transport.py`:
|
||||
|
||||
```python
|
||||
from wf_mcp.storage import AuthStore, CatalogStore
|
||||
```
|
||||
|
||||
Change fields to:
|
||||
|
||||
```python
|
||||
auth_store: AuthStore
|
||||
catalog_store: CatalogStore
|
||||
```
|
||||
|
||||
Update methods:
|
||||
|
||||
```python
|
||||
def save_auth(self, record: AuthRecord) -> None:
|
||||
self.auth_store.save_auth(record)
|
||||
|
||||
def load_auth(self, connection_id: str) -> AuthRecord | None:
|
||||
return self.auth_store.load_auth(connection_id)
|
||||
```
|
||||
|
||||
Replace `self.store.save_catalog(snapshot)` with `self.catalog_store.save_catalog(snapshot)`.
|
||||
|
||||
If tests or callers still inspect `transport.store`, do not keep a fake combined property. Update those tests/callers to use `auth_store` or `catalog_store`; this is the point of the slice.
|
||||
|
||||
- [ ] **Step 4: Update `SourceCatalogService`**
|
||||
|
||||
In `src/wf_mcp/broker/service/source_catalog.py`:
|
||||
|
||||
```python
|
||||
from ...storage import CatalogStore
|
||||
```
|
||||
|
||||
Change:
|
||||
|
||||
```python
|
||||
store: Store
|
||||
```
|
||||
|
||||
to:
|
||||
|
||||
```python
|
||||
store: CatalogStore
|
||||
```
|
||||
|
||||
No behavior change is needed; all current calls on `store` are catalog calls.
|
||||
|
||||
- [ ] **Step 5: Update `WfMcpService` wiring**
|
||||
|
||||
In `src/wf_mcp/broker/service/core.py` import:
|
||||
|
||||
```python
|
||||
from ...storage import AuthStore, CatalogStore, Store
|
||||
```
|
||||
|
||||
Add dataclass fields:
|
||||
|
||||
```python
|
||||
auth_store: AuthStore | None = None
|
||||
catalog_store: CatalogStore | None = None
|
||||
```
|
||||
|
||||
At the top of `__post_init__` after `self.events = ...`, add:
|
||||
|
||||
```python
|
||||
auth_store = self.auth_store or self.store
|
||||
catalog_store = self.catalog_store or self.store
|
||||
```
|
||||
|
||||
Wire:
|
||||
|
||||
```python
|
||||
self.upstream = UpstreamTransportService(
|
||||
auth_store=auth_store,
|
||||
catalog_store=catalog_store,
|
||||
event_sink=self.events.record_event,
|
||||
tool_executor=self.tool_executor,
|
||||
)
|
||||
self.source_catalog = SourceCatalogService(
|
||||
store=catalog_store,
|
||||
...
|
||||
)
|
||||
```
|
||||
|
||||
Keep `store: Store` on `WfMcpService` for compatibility and admin auth provider usage until the next slice removes facade reliance.
|
||||
|
||||
- [ ] **Step 6: Verify focused service tests**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
uv run pytest tests/wf_mcp/service/test_upstream_transport.py tests/wf_mcp/service/test_catalog.py -q
|
||||
```
|
||||
|
||||
Expected: pass.
|
||||
|
||||
---
|
||||
|
||||
## Task 3: Wire Role Roots Into MCP Config Construction
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/wf_mcp/broker/config.py`
|
||||
- Test: `tests/wf_mcp/test_workflow_config_bridge.py`
|
||||
|
||||
- [ ] **Step 1: Strengthen bridge test**
|
||||
|
||||
Update `test_build_service_from_neutral_config_uses_role_store_roots` in `tests/wf_mcp/test_workflow_config_bridge.py` to assert both auth and catalog roots:
|
||||
|
||||
```python
|
||||
assert service.store.root == tmp_path / "auth"
|
||||
assert service.auth_store is not None
|
||||
assert service.catalog_store is not None
|
||||
assert service.auth_store.root == tmp_path / "auth"
|
||||
assert service.catalog_store.root == tmp_path / "catalog"
|
||||
```
|
||||
|
||||
Then add a catalog write assertion:
|
||||
|
||||
```python
|
||||
service.source_catalog.store.save_catalog(
|
||||
CatalogSnapshot(
|
||||
connection_id="everything.default",
|
||||
fetched_at_epoch_ms=1,
|
||||
max_age_seconds=300,
|
||||
nodes=[],
|
||||
resources=[],
|
||||
prompts=[],
|
||||
metadata={},
|
||||
)
|
||||
)
|
||||
assert (
|
||||
tmp_path / "catalog" / "catalog" / "everything.default.json"
|
||||
).exists()
|
||||
assert not (
|
||||
tmp_path / "auth" / "catalog" / "everything.default.json"
|
||||
).exists()
|
||||
```
|
||||
|
||||
Add `CatalogSnapshot` import if missing.
|
||||
|
||||
- [ ] **Step 2: Run failing bridge test**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
uv run pytest tests/wf_mcp/test_workflow_config_bridge.py::test_build_service_from_neutral_config_uses_role_store_roots -q
|
||||
```
|
||||
|
||||
Expected: fail because `build_service_from_config` still passes only `FileStore(auth_root)` to both auth and catalog services.
|
||||
|
||||
- [ ] **Step 3: Construct focused stores in `build_service_from_config`**
|
||||
|
||||
In `src/wf_mcp/broker/config.py`, import:
|
||||
|
||||
```python
|
||||
from ..storage import FileAuthStore, FileCatalogStore, FileStore
|
||||
```
|
||||
|
||||
Update service construction:
|
||||
|
||||
```python
|
||||
auth_store = FileAuthStore(store_roots.auth_root)
|
||||
catalog_store = FileCatalogStore(store_roots.catalog_cache_root)
|
||||
service = WfMcpService(
|
||||
store=FileStore(store_roots.auth_root),
|
||||
auth_store=auth_store,
|
||||
catalog_store=catalog_store,
|
||||
artifact_store=workflow_stores.artifact_store,
|
||||
draft_workspace_store=workflow_stores.draft_workspace_store,
|
||||
run_store=workflow_stores.run_store,
|
||||
tool_executor=McpRuntimePool(runtime_factory.create),
|
||||
)
|
||||
```
|
||||
|
||||
Remove the old comment saying FileStore still owns both auth and catalog. Replace it with:
|
||||
|
||||
```python
|
||||
# Keep FileStore as the compatibility facade on WfMcpService.store while
|
||||
# focused services receive role-specific stores.
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Verify bridge tests**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
uv run pytest tests/wf_mcp/test_workflow_config_bridge.py tests/wf_mcp/test_mcp_workflow_server.py -q
|
||||
```
|
||||
|
||||
Expected: pass.
|
||||
|
||||
---
|
||||
|
||||
## Task 4: Update Admin Auth Provider To Use Focused Auth Store
|
||||
|
||||
**Files:**
|
||||
- Modify: `src/wf_mcp/broker/server.py`
|
||||
- Modify: `src/wf_mcp/broker/service/auth_admin.py`
|
||||
- Test: `tests/wf_mcp/test_mcp_workflow_server.py`
|
||||
|
||||
- [ ] **Step 1: Add wiring assertion**
|
||||
|
||||
In `tests/wf_mcp/test_mcp_workflow_server.py`, add or update a test to assert the admin auth provider uses the service auth store when present:
|
||||
|
||||
```python
|
||||
def test_workflow_server_from_service_uses_focused_auth_store(tmp_path) -> None:
|
||||
auth_store = FileAuthStore(tmp_path / "auth")
|
||||
service = WfMcpService(
|
||||
store=FileStore(tmp_path / "compat"),
|
||||
auth_store=auth_store,
|
||||
artifact_store=FileWorkflowArtifactStore(tmp_path / "workflow"),
|
||||
draft_workspace_store=FileDraftWorkspaceStore(tmp_path / "workflow"),
|
||||
run_store=FileRunStore(tmp_path / "workflow"),
|
||||
)
|
||||
auth_store.save_auth(AuthRecord(connection_id="drive.work", scheme="bearer"))
|
||||
|
||||
server = workflow_server_from_service(service)
|
||||
result = asyncio.run(server.admin.inspect_auth_record("drive.work"))
|
||||
|
||||
assert result["id"] == "drive.work"
|
||||
```
|
||||
|
||||
Follow existing imports/conventions in the file.
|
||||
|
||||
- [ ] **Step 2: Run failing test**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
uv run pytest tests/wf_mcp/test_mcp_workflow_server.py::test_workflow_server_from_service_uses_focused_auth_store -q
|
||||
```
|
||||
|
||||
Expected: fail because `workflow_server_from_service` still builds `McpAuthAdminProvider(store=service.store)`.
|
||||
|
||||
- [ ] **Step 3: Widen auth admin provider type**
|
||||
|
||||
In `src/wf_mcp/broker/service/auth_admin.py`, import `AuthStore` and change:
|
||||
|
||||
```python
|
||||
store: FileStore
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```python
|
||||
store: Store
|
||||
```
|
||||
|
||||
to:
|
||||
|
||||
```python
|
||||
store: AuthStore
|
||||
```
|
||||
|
||||
- [ ] **Step 4: Use focused auth store in server composition**
|
||||
|
||||
In `src/wf_mcp/broker/server.py`, change:
|
||||
|
||||
```python
|
||||
auth=McpAuthAdminProvider(store=service.store),
|
||||
```
|
||||
|
||||
to:
|
||||
|
||||
```python
|
||||
auth=McpAuthAdminProvider(store=service.auth_store or service.store),
|
||||
```
|
||||
|
||||
- [ ] **Step 5: Verify server tests**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
uv run pytest tests/wf_mcp/test_mcp_workflow_server.py tests/wf_mcp/service/test_auth_admin.py -q
|
||||
```
|
||||
|
||||
Expected: pass.
|
||||
|
||||
---
|
||||
|
||||
## Task 5: Docs, Compatibility, And Final Verification
|
||||
|
||||
**Files:**
|
||||
- Modify: `docs/current_roadmap.md`
|
||||
- Modify: `docs/superpowers/specs/2026-06-03-workflow-config-targets-and-sources.md`
|
||||
- Modify: `docs/wf_cli.md`
|
||||
|
||||
- [ ] **Step 1: Update docs**
|
||||
|
||||
In `docs/current_roadmap.md`, replace the role-store limitation sentence with:
|
||||
|
||||
```markdown
|
||||
Follow-up complete: MCP compatibility auth and catalog/cache stores are now
|
||||
split at the service boundary. `FileStore` remains as a compatibility
|
||||
wrapper, while neutral config role roots can drive `FileAuthStore` and
|
||||
`FileCatalogStore` separately.
|
||||
```
|
||||
|
||||
In `docs/superpowers/specs/2026-06-03-workflow-config-targets-and-sources.md`, update the Store Roles status:
|
||||
|
||||
```markdown
|
||||
Implementation status: filesystem role overrides are implemented. MCP auth and
|
||||
catalog/cache storage now have separate file-store adapters; `server.store`
|
||||
still remains the fallback for missing roles.
|
||||
```
|
||||
|
||||
In `docs/wf_cli.md`, keep the user-facing wording short:
|
||||
|
||||
```markdown
|
||||
For filesystem configs, role-specific store overrides can split local/dev auth
|
||||
records and catalog cache from workflow records.
|
||||
```
|
||||
|
||||
- [ ] **Step 2: Run focused tests**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
uv run pytest tests/wf_mcp/test_store.py tests/wf_mcp/service/test_upstream_transport.py tests/wf_mcp/service/test_catalog.py tests/wf_mcp/service/test_auth_admin.py tests/wf_mcp/test_workflow_config_bridge.py tests/wf_mcp/test_mcp_workflow_server.py -q
|
||||
```
|
||||
|
||||
Expected: pass.
|
||||
|
||||
- [ ] **Step 3: Run lint/type checks**
|
||||
|
||||
Run:
|
||||
|
||||
```bash
|
||||
uv run ruff check src tests
|
||||
uv run basedpyright --level error src
|
||||
```
|
||||
|
||||
Expected: ruff passes and basedpyright reports `0 errors`.
|
||||
|
||||
- [ ] **Step 4: Optional full suite**
|
||||
|
||||
Run if time allows:
|
||||
|
||||
```bash
|
||||
uv run pytest -q
|
||||
```
|
||||
|
||||
Expected: current suite shape, around `1202 passed, 1 skipped, 1 xfailed`.
|
||||
|
||||
- [ ] **Step 5: Final report**
|
||||
|
||||
Report:
|
||||
|
||||
- files changed
|
||||
- test/lint/type output
|
||||
- whether full suite was run
|
||||
- remaining compatibility shims (`FileStore`, `WfMcpService.store`)
|
||||
- no on-disk JSON shape changes
|
||||
|
||||
---
|
||||
|
||||
## Self-Review Notes
|
||||
|
||||
- This slice makes `catalog_cache_root` real without changing config shape again.
|
||||
- Backwards compatibility remains: `FileStore(root)` and `WfMcpService(store=...)` still work.
|
||||
- The split is MCP-internal. Neutral `wf_api` remains untouched.
|
||||
- Future package split (`wf_sources_mcp`) becomes easier after this because upstream MCP source code no longer receives a monolithic auth/catalog store.
|
||||
Reference in New Issue
Block a user