From 1a0427c50ae0b261ef19b49b696bdf894fc2add3 Mon Sep 17 00:00:00 2001 From: Egor Merkushev Date: Sat, 7 Mar 2026 12:49:46 +0300 Subject: [PATCH 1/7] Branch for P6-T1: add explicit broker runtime status surface From d989b5e721ab10be3a659b8a2f8380e1877ea176 Mon Sep 17 00:00:00 2001 From: Egor Merkushev Date: Sat, 7 Mar 2026 12:51:15 +0300 Subject: [PATCH 2/7] Select task P6-T1: Add explicit broker runtime status surface for frontend consumers --- SPECS/INPROGRESS/next.md | 24 +++++++++------------- SPECS/Workplan.md | 44 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 14 deletions(-) diff --git a/SPECS/INPROGRESS/next.md b/SPECS/INPROGRESS/next.md index ab5526b..e199028 100644 --- a/SPECS/INPROGRESS/next.md +++ b/SPECS/INPROGRESS/next.md @@ -1,19 +1,15 @@ -# Next Task: None — all tracked tasks completed +# Next Task: P6-T1 — Add explicit broker runtime status surface for frontend consumers -**Priority:** — -**Phase:** — -**Effort:** — -**Dependencies:** — -**Status:** All done +**Priority:** P1 +**Phase:** Phase 6: Explicit Broker Frontend +**Effort:** 4h +**Dependencies:** none +**Status:** Selected -## Recently Archived +## Description -- **P1-T12** (2026-03-07): Improve troubleshooting docs for Zed broker startup timeouts — PASS -- **P5-T2** (2026-03-06): Release 0.4.1 to PyPI and MCP Registry — PASS -- **BUG-T9** (2026-03-06): Fix broker daemon not sending notifications/initialized before tools/list probe — PASS -- **P5-T1** (2026-03-06): Release 0.4.0 to PyPI and MCP Registry — PASS +Add a structured runtime status surface for the persistent broker so explicit frontends do not need to infer daemon health from pid files and log parsing alone. The surface should expose broker lifecycle state, upstream pid/availability, client session counts, and other operator-facing details that explain whether the daemon is healthy, reconnecting, or awaiting approval. -## Description +## Next Step -All tasks in `SPECS/Workplan.md` have been completed. No next task is available. -To add new work, use the `workplan-task-ops` skill or edit `SPECS/Workplan.md` directly. +Run the PLAN command to generate the implementation-ready PRD. diff --git a/SPECS/Workplan.md b/SPECS/Workplan.md index 98b0b29..779714a 100644 --- a/SPECS/Workplan.md +++ b/SPECS/Workplan.md @@ -419,3 +419,47 @@ Add new tasks using the canonical template in [TASK_TEMPLATE.md](TASK_TEMPLATE.m - [ ] MCP Registry entry reflects `0.4.0` (auto-triggered by tag push via CI/CD) - [ ] README version badge displays `v0.4.0` after PyPI publish propagates (auto after tag push) - [x] All quality gates pass on the tagged commit (`pytest` 785 tests, 90.91% coverage, `ruff`, `mypy`, DocC sync, package assets check) + +### Phase 6: Explicit Broker Frontend + +#### ⬜️ P6-T1: Add explicit broker runtime status surface for frontend consumers **INPROGRESS** +- **Description:** Add a structured runtime status surface for the persistent broker so explicit frontends do not need to infer daemon health from pid files and log parsing alone. The surface should expose broker lifecycle state, upstream pid/availability, client session counts, and other operator-facing details that explain whether the daemon is healthy, reconnecting, or awaiting approval. +- **Priority:** P1 +- **Dependencies:** none +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - `src/mcpbridge_wrapper/broker/daemon.py` runtime status data extended for operator-facing consumption + - `src/mcpbridge_wrapper/webui/server.py` status/control endpoint(s) updated to expose broker runtime details + - `tests/unit/webui/` and/or broker tests covering ready/reconnecting/no-upstream status cases +- **Acceptance Criteria:** + - [ ] Dedicated broker host exposes structured runtime status including broker state, daemon pid, upstream pid (when present), version, and connected client count + - [ ] Status makes reconnecting/not-ready states explicit so a frontend can distinguish them from a healthy shared daemon + - [ ] Automated tests cover both healthy and degraded broker runtime status responses + +#### ⬜️ P6-T2: Build a terminal frontend for broker daemon monitoring and control +- **Description:** Implement a terminal-first operator interface for the broker daemon so users can explicitly see whether the daemon is running, whether upstream Xcode connectivity is healthy, which clients are attached, and what recent reconnect/error events occurred. The interface should give a clearer operational model than auto-spawn alone. +- **Priority:** P1 +- **Dependencies:** P6-T1 +- **Parallelizable:** no +- **Outputs/Artifacts:** + - `src/mcpbridge_wrapper/` TUI entrypoint/module for broker monitoring and control + - Tests covering the TUI status rendering and control integration where practical + - CLI/docs wiring for launching the TUI +- **Acceptance Criteria:** + - [ ] Users can launch a terminal UI from the wrapper package to inspect broker runtime state without tailing logs manually + - [ ] The TUI shows at minimum broker state, daemon/upstream PIDs, connected client count, and recent broker events or reconnect indicators + - [ ] The TUI exposes at least one explicit control action for the daemon lifecycle (for example stop or restart) + +#### ⬜️ P6-T3: Document the explicit dedicated-host frontend workflow +- **Description:** Update the operator docs so the recommended path for multi-editor setups is an explicit dedicated broker host plus a single monitoring frontend. The docs should explain when to prefer a dedicated host over implicit auto-spawn, how to verify that both editors share one daemon, and how the new frontend fits into that workflow. +- **Priority:** P2 +- **Dependencies:** P6-T1, P6-T2 +- **Parallelizable:** yes +- **Outputs/Artifacts:** + - `README.md` dedicated-host guidance updated to mention the explicit frontend + - `docs/broker-mode.md` and related docs updated with the recommended monitoring/control workflow + - Any new frontend usage documentation added under `docs/` +- **Acceptance Criteria:** + - [ ] README explains the dedicated-host + frontend workflow for users who want explicit visibility into daemon health + - [ ] Broker docs describe how to confirm that multiple editors are attached to one shared daemon + - [ ] Frontend launch and troubleshooting steps are documented in a user-facing guide From 2bbac64ecf3c7fee6a926340271f21d150f332cd Mon Sep 17 00:00:00 2001 From: Egor Merkushev Date: Sat, 7 Mar 2026 12:52:34 +0300 Subject: [PATCH 3/7] Plan task P6-T1: Add explicit broker runtime status surface for frontend consumers --- ...e_status_surface_for_frontend_consumers.md | 115 ++++++++++++++++++ 1 file changed, 115 insertions(+) create mode 100644 SPECS/INPROGRESS/P6-T1_Add_explicit_broker_runtime_status_surface_for_frontend_consumers.md diff --git a/SPECS/INPROGRESS/P6-T1_Add_explicit_broker_runtime_status_surface_for_frontend_consumers.md b/SPECS/INPROGRESS/P6-T1_Add_explicit_broker_runtime_status_surface_for_frontend_consumers.md new file mode 100644 index 0000000..df29766 --- /dev/null +++ b/SPECS/INPROGRESS/P6-T1_Add_explicit_broker_runtime_status_surface_for_frontend_consumers.md @@ -0,0 +1,115 @@ +# P6-T1 — Add explicit broker runtime status surface for frontend consumers + +## Objective Summary + +The broker already has internal lifecycle state and the Web UI already exposes +basic control metadata, but operators still have to infer real daemon health +from PID files, `--broker-status`, and raw log lines. This task adds one +explicit runtime status surface that frontend consumers can trust. The result +must make it obvious whether the dedicated broker host is healthy, reconnecting, +missing an upstream process, or serving active client sessions. + +This task does not implement the TUI itself. It delivers the structured runtime +contract that a TUI or other explicit frontend can consume without log parsing. + +## Deliverables + +- Extend broker runtime status payloads to include operator-facing fields beyond + the current `state` / `pid` / `upstream_pid` / `version` tuple. +- Expose that payload through the Web UI server in a dedicated API route rather + than overloading `/api/control`. +- Cover ready, degraded, and reconnecting status states with automated tests. + +## Success Criteria + +- A dedicated broker host exposes structured status including broker state, + daemon PID, upstream PID when present, version, and connected client count. +- The payload clearly distinguishes healthy vs reconnecting / not-ready states. +- Frontend consumers can detect whether upstream initialization has completed + without reading `broker.log`. +- Existing control endpoints remain backward-compatible. + +## Test-First Plan + +1. Add Web UI server tests for a new broker-status endpoint and assert the JSON + schema in both default/no-runtime and broker-runtime-backed modes. +2. Add or extend broker daemon tests for richer `status()` payloads, including + connected session count and readiness indicators. +3. Implement the production changes only after the expected payload shape is + fixed in tests. +4. Run full quality gates after implementation: `pytest`, `ruff check src/`, + `mypy src/`, and `pytest --cov`. + +## Execution Plan + +### Phase 1: Define the runtime contract + +Inputs: +- Existing `BrokerDaemon.status()` behavior +- Existing `/api/control`, `/api/config`, and `/api/sessions` routes + +Outputs: +- Final status schema for frontend consumers +- Decision on how broker runtime is injected into the Web UI app + +Verification: +- The schema is stable enough to power a future TUI without additional parsing +- Backward-compatible control API behavior is preserved + +### Phase 2: Implement daemon + Web UI wiring + +Inputs: +- `src/mcpbridge_wrapper/broker/daemon.py` +- `src/mcpbridge_wrapper/webui/server.py` +- broker-daemon startup path in `src/mcpbridge_wrapper/__main__.py` + +Outputs: +- Enriched daemon status payload +- Dedicated Web UI broker-status route +- Broker daemon startup passes a runtime status provider/callback into the UI + +Verification: +- A running dedicated host returns live daemon/runtime details via HTTP +- Non-broker or dashboard-only runtimes fail gracefully with a clear empty or + unavailable status response instead of crashing + +### Phase 3: Lock the behavior with tests and validation + +Inputs: +- New endpoint implementation +- Existing server and broker daemon test suites + +Outputs: +- Unit tests for the endpoint and status payload +- Validation report with quality gate results + +Verification: +- Tests cover healthy and degraded states +- Coverage remains at or above project threshold + +## Acceptance Tests + +- `pytest tests/unit/test_broker_daemon.py -k status` +- `pytest tests/unit/webui/test_server.py -k broker` +- `pytest` +- `ruff check src/` +- `mypy src/` +- `pytest --cov` + +## Decision Points + +- The status endpoint should report runtime data through a dedicated API route + such as `/api/broker/status`, not by mutating `/api/control`, because control + and observability are separate concerns. +- Session count should come from the transport/runtime object when available so + the value reflects live attached clients rather than historical audit sessions. +- Readiness should be explicit. A frontend needs to know whether the daemon is + merely alive or whether upstream initialization is complete. + +## Notes + +- If the Web UI needs a small refactor to accept an optional runtime status + provider, keep that refactor scoped and test-covered. +- Docs updates for using this new surface belong in `P6-T3`, unless a tiny + endpoint mention is required inline for immediate correctness. +- Review subject name for this task: `broker_runtime_status_surface`. From f9d936b7986e11f89f20be222b8204226643ba39 Mon Sep 17 00:00:00 2001 From: Egor Merkushev Date: Sat, 7 Mar 2026 12:56:46 +0300 Subject: [PATCH 4/7] Implement P6-T1: expose broker runtime status surface for frontend consumers --- SPECS/INPROGRESS/P6-T1_Validation_Report.md | 76 +++++++++++++++++++++ src/mcpbridge_wrapper/__main__.py | 7 ++ src/mcpbridge_wrapper/broker/daemon.py | 18 +++++ src/mcpbridge_wrapper/webui/server.py | 35 ++++++++++ tests/unit/test_broker_daemon.py | 16 +++++ tests/unit/test_main.py | 1 + tests/unit/webui/test_server.py | 41 +++++++++++ 7 files changed, 194 insertions(+) create mode 100644 SPECS/INPROGRESS/P6-T1_Validation_Report.md diff --git a/SPECS/INPROGRESS/P6-T1_Validation_Report.md b/SPECS/INPROGRESS/P6-T1_Validation_Report.md new file mode 100644 index 0000000..678d935 --- /dev/null +++ b/SPECS/INPROGRESS/P6-T1_Validation_Report.md @@ -0,0 +1,76 @@ +# P6-T1 Validation Report + +**Task:** P6-T1 — Add explicit broker runtime status surface for frontend consumers +**Date:** 2026-03-07 +**Verdict:** PASS + +## Summary + +Implemented a dedicated broker runtime status surface for frontend consumers by: + +- enriching `BrokerDaemon.status()` with operator-facing fields +- exposing `GET /api/broker/status` in the Web UI server +- wiring a broker-status provider into broker-daemon Web UI startup +- extending unit tests for daemon status, Web UI status API, and main wiring + +## Files Validated + +- `src/mcpbridge_wrapper/broker/daemon.py` +- `src/mcpbridge_wrapper/webui/server.py` +- `src/mcpbridge_wrapper/__main__.py` +- `tests/unit/test_broker_daemon.py` +- `tests/unit/webui/test_server.py` +- `tests/unit/test_main.py` + +## Targeted Verification + +```bash +PYTHONPATH=src pytest tests/unit/test_broker_daemon.py -k status +``` + +- Result: `3 passed` + +```bash +PYTHONPATH=src pytest tests/unit/webui/test_server.py -k 'broker_status or control' +``` + +- Result: `6 passed` + +```bash +PYTHONPATH=src pytest tests/unit/test_main.py -k 'broker_daemon_webui' +``` + +- Result: `3 passed` + +## Required Quality Gates + +```bash +PYTHONPATH=src pytest +``` + +- Result: `787 passed, 5 skipped in 8.02s` + +```bash +ruff check src/ +``` + +- Result: `All checks passed!` + +```bash +mypy src/ +``` + +- Result: `Success: no issues found in 18 source files` + +```bash +PYTHONPATH=src pytest --cov +``` + +- Result: `787 passed, 5 skipped in 8.94s` +- Coverage: `90.64%` + +## Notes + +- `PYTHONPATH=src` was required for `pytest` in the current local shell because the package is not installed into that interpreter environment. +- Coverage remains above the repository threshold of `90%`. +- Remaining warnings are pre-existing dependency deprecations from `websockets`/`uvicorn`, not regressions introduced by this task. diff --git a/src/mcpbridge_wrapper/__main__.py b/src/mcpbridge_wrapper/__main__.py index 9ae83e0..9255827 100644 --- a/src/mcpbridge_wrapper/__main__.py +++ b/src/mcpbridge_wrapper/__main__.py @@ -618,6 +618,12 @@ def main() -> int: metrics = None audit = None + def get_broker_status() -> Optional[Dict[str, Any]]: + """Return live broker runtime status for explicit frontend consumers.""" + if daemon is None: + return None + return daemon.status() + if web_ui_enabled: runtime = _prepare_webui_runtime( web_ui_port=web_ui_port, @@ -656,6 +662,7 @@ def request_broker_shutdown() -> None: audit, service_name="broker-daemon", request_stop=request_broker_shutdown, + broker_status_provider=get_broker_status, ) print( f"Web UI dashboard started at http://{config.host}:{config.port}", diff --git a/src/mcpbridge_wrapper/broker/daemon.py b/src/mcpbridge_wrapper/broker/daemon.py index e3308fc..c02f823 100644 --- a/src/mcpbridge_wrapper/broker/daemon.py +++ b/src/mcpbridge_wrapper/broker/daemon.py @@ -114,13 +114,31 @@ def upstream_initialized(self) -> asyncio.Event: def status(self) -> dict[str, Any]: """Return a dictionary describing the current daemon status.""" upstream_pid: int | None = None + upstream_alive = False if self._upstream is not None: with contextlib.suppress(Exception): upstream_pid = self._upstream.pid + with contextlib.suppress(Exception): + upstream_alive = self._upstream.returncode is None + + connected_clients = 0 + if self._transport is not None: + with contextlib.suppress(Exception): + connected_clients = len(self._transport.sessions) + return { "state": self._state.value, "pid": os.getpid(), "upstream_pid": upstream_pid, + "upstream_alive": upstream_alive, + "upstream_initialized": self._upstream_initialized.is_set(), + "tools_list_cached": self._tools_list_cache is not None, + "connected_clients": connected_clients, + "reconnect_attempt": self._reconnect_attempt, + "shutdown_requested": self._shutdown_requested, + "socket_path": str(self._config.socket_path), + "pid_file": str(self._config.pid_file), + "version_file": str(self._config.version_file), "version": __version__, } diff --git a/src/mcpbridge_wrapper/webui/server.py b/src/mcpbridge_wrapper/webui/server.py index d87d5d5..cbec0aa 100644 --- a/src/mcpbridge_wrapper/webui/server.py +++ b/src/mcpbridge_wrapper/webui/server.py @@ -165,6 +165,7 @@ def create_app( audit: AuditLogger, service_name: str = "mcpbridge-wrapper", request_stop: Callable[[], None] | None = None, + broker_status_provider: Callable[[], dict[str, Any] | None] | None = None, ) -> FastAPI: """Create and configure the FastAPI application. @@ -174,6 +175,7 @@ def create_app( audit: Audit logger instance. service_name: Runtime service label exposed by control API. request_stop: Optional callback used by control API to request shutdown. + broker_status_provider: Optional callback returning broker runtime status. Returns: Configured FastAPI application. @@ -191,6 +193,7 @@ def create_app( app.state.audit = audit app.state.service_name = service_name app.state.request_stop = request_stop + app.state.broker_status_provider = broker_status_provider ws_clients: list[WebSocket] = [] app.state.ws_clients = ws_clients @@ -368,6 +371,32 @@ async def stop_service(request: Request) -> dict[str, str]: "message": f"Shutdown requested for {service_name}.", } + @app.get("/api/broker/status") + async def get_broker_status(request: Request) -> dict[str, Any]: + """Get structured broker runtime status when this runtime can provide it.""" + _check_auth(request, config) + if broker_status_provider is None: + return {"available": False, "service_name": service_name, "broker": None} + + try: + broker_status = broker_status_provider() + except Exception as exc: + return { + "available": False, + "service_name": service_name, + "broker": None, + "error": str(exc), + } + + if broker_status is None: + return {"available": False, "service_name": service_name, "broker": None} + + return { + "available": True, + "service_name": service_name, + "broker": broker_status, + } + # --- API: Health --- @app.get("/api/health") @@ -419,6 +448,7 @@ def run_server( on_started: Callable[[], None] | None = None, service_name: str = "mcpbridge-wrapper", request_stop: Callable[[], None] | None = None, + broker_status_provider: Callable[[], dict[str, Any] | None] | None = None, ) -> None: """Start the web UI server (blocking). @@ -429,6 +459,7 @@ def run_server( on_started: Optional callback invoked after server starts. service_name: Runtime service label exposed by control API. request_stop: Optional callback used by control API to request shutdown. + broker_status_provider: Optional callback returning broker runtime status. """ _require_webui_deps() assert uvicorn is not None @@ -438,6 +469,7 @@ def run_server( audit, service_name=service_name, request_stop=request_stop, + broker_status_provider=broker_status_provider, ) server_config = uvicorn.Config( @@ -486,6 +518,7 @@ def run_server_in_thread( audit: AuditLogger, service_name: str = "mcpbridge-wrapper", request_stop: Callable[[], None] | None = None, + broker_status_provider: Callable[[], dict[str, Any] | None] | None = None, ) -> threading.Thread: """Start the web UI server in a daemon thread. @@ -495,6 +528,7 @@ def run_server_in_thread( audit: Audit logger instance. service_name: Runtime service label exposed by control API. request_stop: Optional callback used by control API to request shutdown. + broker_status_provider: Optional callback returning broker runtime status. Returns: The daemon thread running the server. @@ -508,6 +542,7 @@ def run_server_in_thread( "audit": audit, "service_name": service_name, "request_stop": request_stop, + "broker_status_provider": broker_status_provider, }, daemon=True, name="webui-server", diff --git a/tests/unit/test_broker_daemon.py b/tests/unit/test_broker_daemon.py index 2747043..4066f7a 100644 --- a/tests/unit/test_broker_daemon.py +++ b/tests/unit/test_broker_daemon.py @@ -72,6 +72,12 @@ def test_status_before_start(self, tmp_path: Path) -> None: assert status["state"] == "init" assert status["pid"] == os.getpid() assert status["upstream_pid"] is None + assert status["upstream_alive"] is False + assert status["upstream_initialized"] is False + assert status["tools_list_cached"] is False + assert status["connected_clients"] == 0 + assert status["socket_path"] == str(cfg.socket_path) + assert status["pid_file"] == str(cfg.pid_file) # --------------------------------------------------------------------------- @@ -510,11 +516,21 @@ async def _block(*a, **kw) -> bytes: # type: ignore[no-untyped-def] new=AsyncMock(return_value=proc), ): await daemon.start() + daemon._upstream_initialized.set() + daemon._tools_list_cache = '{"jsonrpc":"2.0","result":{"tools":[]}}' + daemon._transport = MagicMock() + daemon._transport.sessions = {1: MagicMock(), 2: MagicMock()} s = daemon.status() assert s["state"] == "ready" assert s["upstream_pid"] == proc.pid assert s["pid"] == os.getpid() + assert s["upstream_alive"] is True + assert s["upstream_initialized"] is True + assert s["tools_list_cached"] is True + assert s["connected_clients"] == 2 + assert s["socket_path"] == str(cfg.socket_path) + assert s["pid_file"] == str(cfg.pid_file) daemon._stop_event.set() if daemon._read_task and not daemon._read_task.done(): diff --git a/tests/unit/test_main.py b/tests/unit/test_main.py index b3ca516..8132a54 100644 --- a/tests/unit/test_main.py +++ b/tests/unit/test_main.py @@ -1376,6 +1376,7 @@ def test_main_broker_daemon_webui_wires_metrics_and_audit_into_transport(self): metrics, audit, service_name="broker-daemon", + broker_status_provider=ANY, request_stop=ANY, ) request_stop = run_server_in_thread.call_args.kwargs["request_stop"] diff --git a/tests/unit/webui/test_server.py b/tests/unit/webui/test_server.py index ca1ba89..040d421 100644 --- a/tests/unit/webui/test_server.py +++ b/tests/unit/webui/test_server.py @@ -265,6 +265,47 @@ def test_control_capability_default_disables_stop(self, client): assert data["service_name"] == "mcpbridge-wrapper" assert data["can_stop"] is False + def test_broker_status_default_reports_unavailable(self, client): + """Broker status API reports unavailable when no runtime provider is wired.""" + response = client.get("/api/broker/status") + assert response.status_code == 200 + assert response.json() == { + "available": False, + "service_name": "mcpbridge-wrapper", + "broker": None, + } + + def test_broker_status_returns_runtime_payload(self, config, metrics, audit): + """Broker status API returns the provider payload when wired.""" + broker_status = { + "state": "ready", + "pid": 1234, + "upstream_pid": 5678, + "upstream_alive": True, + "upstream_initialized": True, + "tools_list_cached": True, + "connected_clients": 2, + "socket_path": "/tmp/broker.sock", + "pid_file": "/tmp/broker.pid", + "version": "0.4.1", + } + app = create_app( + config, + metrics, + audit, + service_name="broker-daemon", + broker_status_provider=lambda: broker_status, + ) + client = TestClient(app) + + response = client.get("/api/broker/status") + assert response.status_code == 200 + assert response.json() == { + "available": True, + "service_name": "broker-daemon", + "broker": broker_status, + } + def test_control_stop_unsupported_returns_conflict(self, client): """Stop requests return 409 when shutdown callback is not configured.""" response = client.post("/api/control/stop") From b68ccf51e167b0358a1e2c4907dfb7b71d8b9595 Mon Sep 17 00:00:00 2001 From: Egor Merkushev Date: Sat, 7 Mar 2026 13:00:50 +0300 Subject: [PATCH 5/7] Archive task P6-T1: Add explicit broker runtime status surface for frontend consumers (PASS) --- SPECS/ARCHIVE/INDEX.md | 4 +++- ...time_status_surface_for_frontend_consumers.md | 4 ++++ .../P6-T1_Validation_Report.md | 0 SPECS/INPROGRESS/next.md | 15 +++++++++------ SPECS/Workplan.md | 16 +++++++++------- 5 files changed, 25 insertions(+), 14 deletions(-) rename SPECS/{INPROGRESS => ARCHIVE/P6-T1_Add_explicit_broker_runtime_status_surface_for_frontend_consumers}/P6-T1_Add_explicit_broker_runtime_status_surface_for_frontend_consumers.md (98%) rename SPECS/{INPROGRESS => ARCHIVE/P6-T1_Add_explicit_broker_runtime_status_surface_for_frontend_consumers}/P6-T1_Validation_Report.md (100%) diff --git a/SPECS/ARCHIVE/INDEX.md b/SPECS/ARCHIVE/INDEX.md index 836670b..2522bd3 100644 --- a/SPECS/ARCHIVE/INDEX.md +++ b/SPECS/ARCHIVE/INDEX.md @@ -1,11 +1,12 @@ # mcpbridge-wrapper Tasks Archive -**Last Updated:** 2026-03-07 (P1-T12 archived) +**Last Updated:** 2026-03-07 (P6-T1 archived) ## Archived Tasks | Task ID | Folder | Archived | Verdict | |---------|--------|----------|---------| +| P6-T1 | [P6-T1_Add_explicit_broker_runtime_status_surface_for_frontend_consumers/](P6-T1_Add_explicit_broker_runtime_status_surface_for_frontend_consumers/) | 2026-03-07 | PASS | | P1-T12 | [P1-T12_Improve_troubleshooting_docs_for_Zed_broker_startup_timeouts/](P1-T12_Improve_troubleshooting_docs_for_Zed_broker_startup_timeouts/) | 2026-03-07 | PASS | | P5-T2 | [P5-T2_Release_0.4.1_to_PyPI_and_MCP_Registry/](P5-T2_Release_0.4.1_to_PyPI_and_MCP_Registry/) | 2026-03-06 | PASS | | BUG-T9 | [BUG-T9_Fix_broker_daemon_not_sending_notifications_initialized_before_tools_list_probe/](BUG-T9_Fix_broker_daemon_not_sending_notifications_initialized_before_tools_list_probe/) | 2026-03-06 | PASS | @@ -329,6 +330,7 @@ | Date | Task ID | Action | |------|---------|--------| +| 2026-03-07 | P6-T1 | Archived task artifacts and validation report | | 2026-03-07 | P1-T12 | Archived task artifacts and validation report | | 2026-03-06 | P5-T2 | Archived Release_0.4.1_to_PyPI_and_MCP_Registry (PASS) | | 2026-03-06 | P5-T2 | Archived REVIEW_release-0.4.1 report | diff --git a/SPECS/INPROGRESS/P6-T1_Add_explicit_broker_runtime_status_surface_for_frontend_consumers.md b/SPECS/ARCHIVE/P6-T1_Add_explicit_broker_runtime_status_surface_for_frontend_consumers/P6-T1_Add_explicit_broker_runtime_status_surface_for_frontend_consumers.md similarity index 98% rename from SPECS/INPROGRESS/P6-T1_Add_explicit_broker_runtime_status_surface_for_frontend_consumers.md rename to SPECS/ARCHIVE/P6-T1_Add_explicit_broker_runtime_status_surface_for_frontend_consumers/P6-T1_Add_explicit_broker_runtime_status_surface_for_frontend_consumers.md index df29766..71ef5f7 100644 --- a/SPECS/INPROGRESS/P6-T1_Add_explicit_broker_runtime_status_surface_for_frontend_consumers.md +++ b/SPECS/ARCHIVE/P6-T1_Add_explicit_broker_runtime_status_surface_for_frontend_consumers/P6-T1_Add_explicit_broker_runtime_status_surface_for_frontend_consumers.md @@ -113,3 +113,7 @@ Verification: - Docs updates for using this new surface belong in `P6-T3`, unless a tiny endpoint mention is required inline for immediate correctness. - Review subject name for this task: `broker_runtime_status_surface`. + +--- +**Archived:** 2026-03-07 +**Verdict:** PASS diff --git a/SPECS/INPROGRESS/P6-T1_Validation_Report.md b/SPECS/ARCHIVE/P6-T1_Add_explicit_broker_runtime_status_surface_for_frontend_consumers/P6-T1_Validation_Report.md similarity index 100% rename from SPECS/INPROGRESS/P6-T1_Validation_Report.md rename to SPECS/ARCHIVE/P6-T1_Add_explicit_broker_runtime_status_surface_for_frontend_consumers/P6-T1_Validation_Report.md diff --git a/SPECS/INPROGRESS/next.md b/SPECS/INPROGRESS/next.md index e199028..2b31432 100644 --- a/SPECS/INPROGRESS/next.md +++ b/SPECS/INPROGRESS/next.md @@ -1,15 +1,18 @@ -# Next Task: P6-T1 — Add explicit broker runtime status surface for frontend consumers +# Next Task: P6-T2 — Build a terminal frontend for broker daemon monitoring and control **Priority:** P1 **Phase:** Phase 6: Explicit Broker Frontend -**Effort:** 4h -**Dependencies:** none -**Status:** Selected +**Dependencies:** P6-T1 +**Status:** Ready ## Description -Add a structured runtime status surface for the persistent broker so explicit frontends do not need to infer daemon health from pid files and log parsing alone. The surface should expose broker lifecycle state, upstream pid/availability, client session counts, and other operator-facing details that explain whether the daemon is healthy, reconnecting, or awaiting approval. +Implement a terminal-first operator interface for the broker daemon so users can explicitly see whether the daemon is running, whether upstream Xcode connectivity is healthy, which clients are attached, and what recent reconnect/error events occurred. The interface should give a clearer operational model than auto-spawn alone. + +## Recently Archived + +- `P6-T1` — Add explicit broker runtime status surface for frontend consumers (`PASS`, archived 2026-03-07) ## Next Step -Run the PLAN command to generate the implementation-ready PRD. +Run the PLAN command for `P6-T2` and define the TUI scope, entrypoint, and control flow against the new broker runtime status API. diff --git a/SPECS/Workplan.md b/SPECS/Workplan.md index 779714a..8917c96 100644 --- a/SPECS/Workplan.md +++ b/SPECS/Workplan.md @@ -422,19 +422,21 @@ Add new tasks using the canonical template in [TASK_TEMPLATE.md](TASK_TEMPLATE.m ### Phase 6: Explicit Broker Frontend -#### ⬜️ P6-T1: Add explicit broker runtime status surface for frontend consumers **INPROGRESS** +#### ✅ P6-T1: Add explicit broker runtime status surface for frontend consumers +- **Status:** ✅ Completed (2026-03-07) - **Description:** Add a structured runtime status surface for the persistent broker so explicit frontends do not need to infer daemon health from pid files and log parsing alone. The surface should expose broker lifecycle state, upstream pid/availability, client session counts, and other operator-facing details that explain whether the daemon is healthy, reconnecting, or awaiting approval. - **Priority:** P1 - **Dependencies:** none - **Parallelizable:** yes - **Outputs/Artifacts:** - - `src/mcpbridge_wrapper/broker/daemon.py` runtime status data extended for operator-facing consumption - - `src/mcpbridge_wrapper/webui/server.py` status/control endpoint(s) updated to expose broker runtime details - - `tests/unit/webui/` and/or broker tests covering ready/reconnecting/no-upstream status cases + - `src/mcpbridge_wrapper/broker/daemon.py` runtime status payload extended with readiness, upstream-health, and connected-client details + - `src/mcpbridge_wrapper/webui/server.py` new `GET /api/broker/status` endpoint for frontend consumers + - `src/mcpbridge_wrapper/__main__.py` broker-daemon Web UI wiring updated to publish live runtime status + - `tests/unit/test_broker_daemon.py`, `tests/unit/webui/test_server.py`, and `tests/unit/test_main.py` covering healthy and degraded runtime status flows - **Acceptance Criteria:** - - [ ] Dedicated broker host exposes structured runtime status including broker state, daemon pid, upstream pid (when present), version, and connected client count - - [ ] Status makes reconnecting/not-ready states explicit so a frontend can distinguish them from a healthy shared daemon - - [ ] Automated tests cover both healthy and degraded broker runtime status responses + - [x] Dedicated broker host exposes structured runtime status including broker state, daemon pid, upstream pid (when present), version, and connected client count + - [x] Status makes reconnecting/not-ready states explicit so a frontend can distinguish them from a healthy shared daemon + - [x] Automated tests cover both healthy and degraded broker runtime status responses #### ⬜️ P6-T2: Build a terminal frontend for broker daemon monitoring and control - **Description:** Implement a terminal-first operator interface for the broker daemon so users can explicitly see whether the daemon is running, whether upstream Xcode connectivity is healthy, which clients are attached, and what recent reconnect/error events occurred. The interface should give a clearer operational model than auto-spawn alone. From b81a7ecda6626d50c096e176304805d76ac76e56 Mon Sep 17 00:00:00 2001 From: Egor Merkushev Date: Sat, 7 Mar 2026 13:02:55 +0300 Subject: [PATCH 6/7] Review P6-T1: broker runtime status surface --- .../P6-T1_Validation_Report.md | 4 +- .../REVIEW_broker_runtime_status_surface.md | 44 +++++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) create mode 100644 SPECS/INPROGRESS/REVIEW_broker_runtime_status_surface.md diff --git a/SPECS/ARCHIVE/P6-T1_Add_explicit_broker_runtime_status_surface_for_frontend_consumers/P6-T1_Validation_Report.md b/SPECS/ARCHIVE/P6-T1_Add_explicit_broker_runtime_status_surface_for_frontend_consumers/P6-T1_Validation_Report.md index 678d935..1c4e35e 100644 --- a/SPECS/ARCHIVE/P6-T1_Add_explicit_broker_runtime_status_surface_for_frontend_consumers/P6-T1_Validation_Report.md +++ b/SPECS/ARCHIVE/P6-T1_Add_explicit_broker_runtime_status_surface_for_frontend_consumers/P6-T1_Validation_Report.md @@ -1,7 +1,7 @@ # P6-T1 Validation Report -**Task:** P6-T1 — Add explicit broker runtime status surface for frontend consumers -**Date:** 2026-03-07 +**Task:** P6-T1 — Add explicit broker runtime status surface for frontend consumers +**Date:** 2026-03-07 **Verdict:** PASS ## Summary diff --git a/SPECS/INPROGRESS/REVIEW_broker_runtime_status_surface.md b/SPECS/INPROGRESS/REVIEW_broker_runtime_status_surface.md new file mode 100644 index 0000000..9ad7d3d --- /dev/null +++ b/SPECS/INPROGRESS/REVIEW_broker_runtime_status_surface.md @@ -0,0 +1,44 @@ +## REVIEW REPORT — Broker Runtime Status Surface + +**Scope:** `origin/main..HEAD` +**Files:** 11 + +### Summary Verdict +- [x] Approve +- [ ] Approve with comments +- [ ] Request changes +- [ ] Block + +### Critical Issues + +None. + +### Secondary Issues + +None. + +### Architectural Notes + +- The new broker runtime status surface is correctly separated from control + concerns by adding `GET /api/broker/status` instead of mutating + `/api/control`. +- `BrokerDaemon.status()` now exposes enough operator-facing state for an + explicit frontend to distinguish healthy, reconnecting, and not-ready broker + states without reading pid files or parsing logs. +- Broker-daemon Web UI startup keeps the new status surface optional, so + non-broker and dashboard-only runtimes remain backward-compatible. + +### Tests + +- Validation report confirms: + - `PYTHONPATH=src pytest` -> `787 passed, 5 skipped` + - `ruff check src/` -> pass + - `mypy src/` -> pass + - `PYTHONPATH=src pytest --cov` -> `90.64%` +- Targeted status-path tests cover daemon status payloads, Web UI endpoint + responses, and broker-daemon wiring in `__main__.py`. + +### Next Steps + +- FOLLOW-UP skipped: no actionable review findings. +- Proceed to `ARCHIVE-REVIEW` and start planning `P6-T2`. From b8b703361f92c70dbbedb6cce97c83d88626dab1 Mon Sep 17 00:00:00 2001 From: Egor Merkushev Date: Sat, 7 Mar 2026 13:03:24 +0300 Subject: [PATCH 7/7] Archive REVIEW_broker_runtime_status_surface report --- SPECS/ARCHIVE/INDEX.md | 4 +++- .../P6-T1_Validation_Report.md | 4 ++-- .../_Historical}/REVIEW_broker_runtime_status_surface.md | 3 +++ 3 files changed, 8 insertions(+), 3 deletions(-) rename SPECS/{INPROGRESS => ARCHIVE/_Historical}/REVIEW_broker_runtime_status_surface.md (84%) diff --git a/SPECS/ARCHIVE/INDEX.md b/SPECS/ARCHIVE/INDEX.md index 2522bd3..fee60df 100644 --- a/SPECS/ARCHIVE/INDEX.md +++ b/SPECS/ARCHIVE/INDEX.md @@ -1,6 +1,6 @@ # mcpbridge-wrapper Tasks Archive -**Last Updated:** 2026-03-07 (P6-T1 archived) +**Last Updated:** 2026-03-07 (REVIEW_broker_runtime_status_surface archived) ## Archived Tasks @@ -194,6 +194,7 @@ | File | Description | |------|-------------| +| [REVIEW_broker_runtime_status_surface.md](_Historical/REVIEW_broker_runtime_status_surface.md) | Review report for P6-T1 | | [REVIEW_p1_t12_zed_timeout_docs.md](_Historical/REVIEW_p1_t12_zed_timeout_docs.md) | Review report for P1-T12 | | [REVIEW_P4-T2_broker_readiness_cache.md](_Historical/REVIEW_P4-T2_broker_readiness_cache.md) | Review report for P4-T2 | | [REVIEW_P1-T11_readme_coverage_badge.md](_Historical/REVIEW_P1-T11_readme_coverage_badge.md) | Review report for P1-T11 | @@ -330,6 +331,7 @@ | Date | Task ID | Action | |------|---------|--------| +| 2026-03-07 | P6-T1 | Archived REVIEW_broker_runtime_status_surface report | | 2026-03-07 | P6-T1 | Archived task artifacts and validation report | | 2026-03-07 | P1-T12 | Archived task artifacts and validation report | | 2026-03-06 | P5-T2 | Archived Release_0.4.1_to_PyPI_and_MCP_Registry (PASS) | diff --git a/SPECS/ARCHIVE/P6-T1_Add_explicit_broker_runtime_status_surface_for_frontend_consumers/P6-T1_Validation_Report.md b/SPECS/ARCHIVE/P6-T1_Add_explicit_broker_runtime_status_surface_for_frontend_consumers/P6-T1_Validation_Report.md index 1c4e35e..678d935 100644 --- a/SPECS/ARCHIVE/P6-T1_Add_explicit_broker_runtime_status_surface_for_frontend_consumers/P6-T1_Validation_Report.md +++ b/SPECS/ARCHIVE/P6-T1_Add_explicit_broker_runtime_status_surface_for_frontend_consumers/P6-T1_Validation_Report.md @@ -1,7 +1,7 @@ # P6-T1 Validation Report -**Task:** P6-T1 — Add explicit broker runtime status surface for frontend consumers -**Date:** 2026-03-07 +**Task:** P6-T1 — Add explicit broker runtime status surface for frontend consumers +**Date:** 2026-03-07 **Verdict:** PASS ## Summary diff --git a/SPECS/INPROGRESS/REVIEW_broker_runtime_status_surface.md b/SPECS/ARCHIVE/_Historical/REVIEW_broker_runtime_status_surface.md similarity index 84% rename from SPECS/INPROGRESS/REVIEW_broker_runtime_status_surface.md rename to SPECS/ARCHIVE/_Historical/REVIEW_broker_runtime_status_surface.md index 9ad7d3d..9625615 100644 --- a/SPECS/INPROGRESS/REVIEW_broker_runtime_status_surface.md +++ b/SPECS/ARCHIVE/_Historical/REVIEW_broker_runtime_status_surface.md @@ -37,6 +37,9 @@ None. - `PYTHONPATH=src pytest --cov` -> `90.64%` - Targeted status-path tests cover daemon status payloads, Web UI endpoint responses, and broker-daemon wiring in `__main__.py`. +- Re-ran focused review checks on this branch: + - `PYTHONPATH=src pytest tests/unit/test_broker_daemon.py -k status -q` -> `3 passed` + - `PYTHONPATH=src pytest tests/unit/webui/test_server.py -k broker_status -q` -> `2 passed` ### Next Steps