feat(BRIDGE-WP-0003): MCP server, /bridge-status skill, cross-mode coverage enforcement

Implements the full BRIDGE-WP-0003 workplan: 188 tests passing, 0 lint errors.

## What's added

**Capability registry** (`src/bridge/capabilities.py`):
- 10 capabilities with required_access_modes (cli/mcp/skill)
- Single source of truth for what OpsBridge does and where

**MCP server** (`src/bridge/mcp_server/server.py`):
- 10 FastMCP tools: bridge_up/down/restart/status/logs + 5 catalog_* tools
- 3 resources: bridge://status, catalog://domains, catalog://targets
- `.mcp.json` for project-scope auto-registration
- `scripts/register_mcp.py` for user-scope machine-global registration

**Skill** (`~/.claude/plugins/ops-bridge/bridge-status.md`):
- /bridge-status: health table with emoji indicators + remediation advice

**Cross-mode test coverage enforcement**:
- `tests/conftest.py`: capability/access_mode marks + collect_capability_coverage()
- `tests/test_mcp.py`: 31 FastMCP in-process client tests (Client(mcp) pattern)
- `tests/test_skill.py`: static skill lint against capability registry
- `tests/test_coverage_completeness.py`: meta-test that fails if any required
  (capability × mode) pair lacks a test; also validates CLI commands and MCP
  tools are registered in the capability registry

**ADR** (`architecture/adr-001-cross-mode-capability-registry.md`):
- Documents the registry pattern and FastMCP 3.x testing approach

Key implementation note: FastMCP 3.x in-process results are in
result.content[0].text (JSON string), not result.data directly.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-12 11:33:16 +01:00
parent 44b5a9426a
commit 365c0d611a
30 changed files with 2845 additions and 47 deletions

View File

@@ -0,0 +1,55 @@
---
id: ADR-001
title: Cross-Mode Capability Registry and Coverage Enforcement
status: accepted
date: 2026-03-12
---
## Context
OpsBridge exposes its operations through three access modes: CLI (`bridge` CLI), MCP server
(FastMCP stdio), and Skills (Claude plugin prompts). As the capability surface grows, there is
no guarantee that a new capability will be implemented consistently across all required modes,
or that tests exist for each mode.
## Decision
Introduce a canonical **Capability Registry** (`src/bridge/capabilities.py`) that:
1. Lists every operation as a `Capability(name, description, required_access_modes)` dataclass.
2. Declares which access modes each capability must support.
3. Is imported by the cross-mode meta-test to enforce complete test coverage.
### Test coverage enforcement
Pytest marks `@pytest.mark.capability(name)` and `@pytest.mark.access_mode(mode)` are placed
on the canonical test for each (capability, mode) pair. `tests/test_coverage_completeness.py`
collects these marks at session scope and fails if any pair required by the registry has no
corresponding test.
### FastMCP in-process testing
MCP tools are tested in `tests/test_mcp.py` using `fastmcp.Client(mcp_app)` — an in-process
client that calls tools without spawning a subprocess or opening a network socket. This is the
preferred approach because:
- Tests run in the same process as the server code, so patches/mocks work normally.
- No port allocation, no cleanup, no flakiness from network timeouts.
- FastMCP 3.x returns results via `result.content[0].text` (JSON string) for non-empty
responses, and `result.data` (empty list/dict) when the return value is empty.
### Skill static lint
`tests/test_skill.py` validates skill Markdown files in `~/.claude/plugins/ops-bridge/`:
- Required frontmatter: `name`, `description`.
- Body must reference at least one registered capability name.
- The `bridge_status` skill must reference `bridge_status` and the registry must declare
`skill` as a required mode for that capability.
## Consequences
- Every new capability must be added to the registry before or alongside its implementation.
- Every new (capability, mode) pair requires a marked test or the meta-test fails.
- The registry is the single source of truth for "what does OpsBridge do and where".
- Skills must reference capability names by their canonical registry IDs.