Skip to content
Merged
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
# FU-P7-T3-1 — Prioritize foreign port-owner guidance in mixed broker/dashboard conflicts

## Objective Summary

`P7-T3` removed the silent “broker alive, dashboard absent” partial state, but
its review found one remaining mixed-state failure mode: when a broker PID is
still live and the requested dashboard port is simultaneously occupied by a
foreign listener, startup guidance prioritizes the broker reset path and can
hide the actual port owner. That can send users into a reset loop while the
real blocker, the unrelated listener on the port, remains untouched.

This follow-up should make mixed-state diagnostics explicit across both startup
and `--doctor`. The goal is not to invent a new recovery workflow. Instead,
the code should surface the foreign port owner first or name both blockers in
one clear remediation path, while preserving the existing single-listener and
broker-without-dashboard behavior for non-mixed states.

## Deliverables

- Update `src/mcpbridge_wrapper/__main__.py` so mixed-state startup failures
can report both a live broker PID and a foreign dashboard-port listener
instead of hiding the listener behind broker-reset guidance.
- Update `src/mcpbridge_wrapper/doctor.py` so mixed broker/listener conflicts
classify as a port-ownership issue rather than the generic
`broker-without-dashboard` diagnosis.
- Add regression coverage in `tests/unit/test_main.py` and
`tests/unit/test_doctor.py` for the mixed-state conflict path.
- Produce `SPECS/INPROGRESS/FU-P7-T3-1_Validation_Report.md` with required
quality-gate evidence.

## Success Criteria

- `--broker-console` and `--broker-daemon --web-ui` mention the foreign
dashboard-port listener or both blockers when a live broker PID and foreign
listener coexist.
- `--doctor` no longer hides the foreign listener behind a generic
broker-without-dashboard diagnosis in the same mixed state.
- Regression tests pin the mixed-state ordering so future changes cannot revert
to broker-only guidance.

## Test-First Plan

1. Add startup tests that model the mixed state for both `_run_broker_console()`
and `main()`’s `--broker-daemon --web-ui` path, asserting the foreign port
owner is surfaced alongside the live broker PID.
2. Add a doctor-classification test for a live local broker plus foreign
listener on the configured dashboard port, asserting the report stays in the
port-occupied family instead of `broker-without-dashboard`.
3. Implement the smallest production changes needed to collect both blockers
before choosing the user-facing guidance path.
4. Run the required quality gates: `pytest`, `ruff check src/`, `mypy src/`,
and `pytest --cov`.

## Execution Plan

### Phase 1: Pin the mixed-state startup contract

Inputs:
- `src/mcpbridge_wrapper/__main__.py`
- existing `P7-T3` startup tests in `tests/unit/test_main.py`

Outputs:
- regression tests for mixed-state broker PID + foreign listener conflicts
- a precise expected stderr contract for broker-console and broker-daemon flows

Verification:
- the new startup tests fail on the current ordering that hides the foreign
listener

### Phase 2: Align startup guidance

Inputs:
- `_run_broker_console()`
- `_report_requested_dashboard_unavailable()`
- broker-daemon web-ui startup branch in `main()`

Outputs:
- startup logic that can see both `running_broker_pid` and `listener_pids`
- one explicit remediation path that surfaces the foreign listener without
dropping broker-state context

Verification:
- startup messaging for single-blocker states remains unchanged
- mixed-state messaging mentions the foreign listener or both blockers

### Phase 3: Align doctor classification and validate

Inputs:
- `src/mcpbridge_wrapper/doctor.py`
- doctor classification tests
- full repo quality gates

Outputs:
- mixed-state doctor report that stays actionable and consistent with startup
- validation report with targeted and full quality-gate evidence

Verification:
- `--doctor` and startup both direct the user toward resolving the foreign port
conflict before or alongside broker reset
- coverage remains at or above the repository threshold

## Acceptance Tests

- `pytest tests/unit/test_main.py`
- `pytest tests/unit/test_doctor.py`
- `pytest`
- `ruff check src/`
- `mypy src/`
- `pytest --cov`

## Decision Points

- Prefer a combined-message approach when both blockers are real; users should
not lose visibility into the live broker PID just because the foreign
listener is now prioritized.
- Keep the public recovery commands aligned with `P7-T3` and `P7-T2` rather
than inventing a new remediation surface for this follow-up.

## Notes

- No documentation changes are expected unless the implementation forces
observable command/help text changes beyond stderr diagnostics.
- Review subject name for this task: `mixed_dashboard_conflict_guidance`.

---
**Archived:** 2026-03-07
**Verdict:** PASS
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# FU-P7-T3-1 Validation Report

**Task:** FU-P7-T3-1 — Prioritize foreign port-owner guidance in mixed broker/dashboard conflicts
**Date:** 2026-03-07
**Verdict:** PASS

## Summary

Implemented the `FU-P7-T3-1` follow-up by:

- updating startup guidance in `src/mcpbridge_wrapper/__main__.py` so mixed
broker/dashboard conflicts can surface both a live broker PID and a foreign
listener on the requested dashboard port
- fixing `_run_broker_console()` so it inspects the configured dashboard port
before returning early on a running broker PID, allowing mixed-state
reporting instead of broker-only guidance
- aligning `src/mcpbridge_wrapper/doctor.py` so mixed broker-plus-listener
states classify as a port-ownership issue rather than being hidden behind the
generic `broker-without-dashboard` diagnosis
- adding regression coverage for broker-console, broker-daemon `--web-ui`, and
doctor mixed-state flows

## Files Validated

- `src/mcpbridge_wrapper/__main__.py`
- `src/mcpbridge_wrapper/doctor.py`
- `tests/unit/test_main.py`
- `tests/unit/test_doctor.py`

## Targeted Verification

```bash
pytest tests/unit/test_main.py tests/unit/test_doctor.py -k 'mixed_state_mentions_foreign_listener or mixed_broker_and_foreign_listener_prefers_port_conflict'
```

- Result: `3 passed`
- Observed outcome: startup and doctor now keep the foreign port owner visible
in mixed broker/listener conflicts

```bash
pytest tests/unit/test_main.py tests/unit/test_doctor.py
```

- Result: `119 passed`

## Required Quality Gates

```bash
pytest
```

- Result: `891 passed, 5 skipped in 8.14s`

```bash
ruff check src/
```

- Result: `All checks passed!`

```bash
mypy src/
```

- Result: `Success: no issues found in 20 source files`

```bash
pytest --cov
```

- Result: `891 passed, 5 skipped in 9.16s`
- Coverage: `91.64%`

## Acceptance Criteria Evidence

- [x] `--broker-console` and `--broker-daemon --web-ui` surface the foreign
dashboard-port owner or both blockers when a live broker PID and foreign
listener coexist.
- Evidence: `tests/unit/test_main.py::TestBrokerConsoleHelpers::test_run_broker_console_mixed_state_mentions_foreign_listener`
and
`tests/unit/test_main.py::TestMainBrokerWebUIFlowCoverage::test_main_broker_daemon_webui_mixed_state_mentions_foreign_listener`
both passed.
- [x] `--doctor` does not hide the foreign listener behind a generic
broker-without-dashboard diagnosis in the same mixed state.
- Evidence:
`tests/unit/test_doctor.py::TestClassifyDoctorReport::test_classify_mixed_broker_and_foreign_listener_prefers_port_conflict`
passed with `report.code == "port-occupied"`.
- [x] Regression tests cover the mixed-state conflict and prevent reordering
back to broker-only guidance.
- Evidence: new mixed-state tests were added in `tests/unit/test_main.py` and
`tests/unit/test_doctor.py`, and the targeted plus full affected-module
suites passed.

## Notes

- The mixed-state fix keeps existing single-blocker behavior unchanged: plain
broker-without-dashboard and plain foreign-listener paths still use their
original guidance.
- Existing `websockets` / `uvicorn` deprecation warnings remain in the suite
and are unrelated to this task.
6 changes: 5 additions & 1 deletion SPECS/ARCHIVE/INDEX.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
# mcpbridge-wrapper Tasks Archive

**Last Updated:** 2026-03-07 (FU-P7-T1-1 review archived)
**Last Updated:** 2026-03-07

## Archived Tasks

| Task ID | Folder | Archived | Verdict |
|---------|--------|----------|---------|
| FU-P7-T3-1 | [FU-P7-T3-1_Prioritize_foreign_port-owner_guidance_in_mixed_broker-dashboard_conflicts/](FU-P7-T3-1_Prioritize_foreign_port-owner_guidance_in_mixed_broker-dashboard_conflicts/) | 2026-03-07 | PASS |
| FU-P7-T1-1 | [FU-P7-T1-1_Normalize_KeyboardInterrupt_handling_when_broker-console_reuses_an_existing_host/](FU-P7-T1-1_Normalize_KeyboardInterrupt_handling_when_broker-console_reuses_an_existing_host/) | 2026-03-07 | PASS |
| P7-T3 | [P7-T3_Auto-recover_or_guide_on_dashboard_port_ownership_conflicts/](P7-T3_Auto-recover_or_guide_on_dashboard_port_ownership_conflicts/) | 2026-03-07 | PASS |
| P7-T2 | [P7-T2_Implement_a_broker_doctor_command_for_cross-black-box_diagnostics/](P7-T2_Implement_a_broker_doctor_command_for_cross-black-box_diagnostics/) | 2026-03-07 | PASS |
Expand Down Expand Up @@ -200,6 +201,7 @@

| File | Description |
|------|-------------|
| [REVIEW_mixed_dashboard_conflict_guidance.md](_Historical/REVIEW_mixed_dashboard_conflict_guidance.md) | Review report for FU-P7-T3-1 |
| [REVIEW_broker_console_keyboardinterrupt_reuse.md](_Historical/REVIEW_broker_console_keyboardinterrupt_reuse.md) | Review report for FU-P7-T1-1 |
| [REVIEW_dashboard_port_ownership_conflicts.md](_Historical/REVIEW_dashboard_port_ownership_conflicts.md) | Review report for P7-T3 |
| [REVIEW_broker_doctor_diagnostics.md](_Historical/REVIEW_broker_doctor_diagnostics.md) | Review report for P7-T2 |
Expand Down Expand Up @@ -343,6 +345,8 @@

| Date | Task ID | Action |
|------|---------|--------|
| 2026-03-07 | FU-P7-T3-1 | Archived REVIEW_mixed_dashboard_conflict_guidance report |
| 2026-03-07 | FU-P7-T3-1 | Archived Prioritize_foreign_port-owner_guidance_in_mixed_broker-dashboard_conflicts (PASS) |
| 2026-03-07 | FU-P7-T1-1 | Archived REVIEW_broker_console_keyboardinterrupt_reuse report |
| 2026-03-07 | FU-P7-T1-1 | Archived Normalize_KeyboardInterrupt_handling_when_broker-console_reuses_an_existing_host (PASS) |
| 2026-03-07 | P7-T1 | Archived REVIEW_broker_console_startup report |
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
## REVIEW REPORT — mixed_dashboard_conflict_guidance

**Scope:** origin/main..HEAD
**Files:** 9

### Summary Verdict
- [ ] Approve
- [x] Approve with comments
- [ ] Request changes
- [ ] Block

### Critical Issues

- [High] `src/mcpbridge_wrapper/__main__.py:613-621`,
`src/mcpbridge_wrapper/__main__.py:761-779`,
`src/mcpbridge_wrapper/doctor.py:437-465`:
the new mixed-state branch treats any listener on the configured dashboard
port as a foreign port conflict whenever a broker PID is also live. That
includes the broker daemon’s own listener if it is still bound to the port
while `/api/control` or `/api/broker/status` is degraded. In that state, the
new guidance can incorrectly tell users to stop the “existing listener” or
use `--web-ui-restart`, even though the listener may be the same broker
process named by the PID file. The fix should distinguish foreign listener
PIDs from the broker’s own PID before taking the mixed-state port-conflict
path.

### Secondary Issues

- [Medium] `tests/unit/test_main.py:1788-1824`,
`tests/unit/test_main.py:2276-2329`,
`tests/unit/test_doctor.py:313-326`:
the new regression tests only cover the foreign-listener case. There is no
test where `listener_pids` equals the broker PID (or contains the local
doctor PID), so the self-listener misclassification above would currently
pass unnoticed. Add same-PID coverage alongside the existing foreign-listener
assertions.

### Architectural Notes

- The task correctly fixed the user-visible blind spot that motivated
`FU-P7-T3-1`: foreign listeners are now surfaced in the mixed-state path
instead of being hidden behind broker-reset guidance.
- The remaining issue is about PID ownership precision, not about whether mixed
state should be surfaced at all. The next iteration should refine the
classifier to separate foreign listeners from broker-owned listeners.

### Tests

- `pytest tests/unit/test_main.py tests/unit/test_doctor.py -k 'mixed_state_mentions_foreign_listener or mixed_broker_and_foreign_listener_prefers_port_conflict'` passed (`3 passed`)
- `pytest tests/unit/test_main.py tests/unit/test_doctor.py` passed (`119 passed`)
- `pytest` passed (`891 passed, 5 skipped`)
- `ruff check src/` passed
- `mypy src/` passed
- `pytest --cov` passed with `91.64%` coverage

### Next Steps

- Add a focused follow-up task to distinguish foreign listener PIDs from the
broker’s own PID in startup and doctor mixed-state guidance.
- Add regression coverage for the broker-owned-listener case so the mixed-state
fix does not regress into self-conflict messaging.
20 changes: 10 additions & 10 deletions SPECS/INPROGRESS/next.md
Original file line number Diff line number Diff line change
@@ -1,26 +1,26 @@
# Next Task: FU-P7-T3-1Prioritize foreign port-owner guidance in mixed broker/dashboard conflicts
# Next Task: FU-P7-T3-2Exclude broker-owned dashboard listeners from foreign port-conflict guidance

**Priority:** P1
**Phase:** Phase 7: Broker UX and Diagnostics
**Effort:** 2-3 hours
**Dependencies:** P7-T3
**Dependencies:** FU-P7-T3-1
**Status:** Ready

## Description

When startup sees both a live broker PID and a non-broker listener on the
requested dashboard port, current remediation prioritizes broker reset guidance
and can hide the actual foreign port owner. Update startup and diagnostics
conflict ordering so users see the real blocker or one combined recovery path
instead of being sent into a reset loop.
Refine the mixed broker/dashboard conflict classifier so it distinguishes the
broker daemon's own dashboard listener from a foreign process on the same port.
When degraded probes occur against a broker-owned listener, startup and
diagnostics should keep users on broker-health guidance instead of reporting a
foreign port owner.

## Recently Archived

- `2026-03-07` — `FU-P7-T3-1` archived with verdict `PASS`
- `2026-03-07` — `FU-P7-T1-1` archived with verdict `PASS`
- `2026-03-07` — `P7-T3` archived with verdict `PASS`
- `2026-03-07` — `P7-T2` archived with verdict `PASS`

## Next Step

Create the `FU-P7-T3-1` PRD in `SPECS/INPROGRESS/`, then implement and
validate the mixed-state dashboard conflict guidance updates.
Create the `FU-P7-T3-2` PRD in `SPECS/INPROGRESS/`, then implement and validate
the broker-owned-listener exclusion in startup and doctor mixed-state guidance.
23 changes: 19 additions & 4 deletions SPECS/Workplan.md
Original file line number Diff line number Diff line change
Expand Up @@ -528,7 +528,8 @@ Add new tasks using the canonical template in [TASK_TEMPLATE.md](TASK_TEMPLATE.m
- [x] Port conflicts result in either automatic safe recovery or one explicit remediation path with exact commands or next steps
- [x] TUI and diagnostics clearly explain the conflict source and whether the current runtime is usable

#### ⬜️ FU-P7-T3-1: Prioritize foreign port-owner guidance in mixed broker/dashboard conflicts
#### ✅ FU-P7-T3-1: Prioritize foreign port-owner guidance in mixed broker/dashboard conflicts
- **Status:** ✅ Completed (2026-03-07)
- **Description:** When startup sees both a live broker PID and a non-broker listener on the requested dashboard port, current remediation prioritizes broker reset guidance and can hide the actual foreign port owner. Update startup and diagnostics conflict ordering so users see the real blocker or one combined recovery path instead of being sent into a reset loop.
- **Priority:** P1
- **Dependencies:** P7-T3
Expand All @@ -538,9 +539,23 @@ Add new tasks using the canonical template in [TASK_TEMPLATE.md](TASK_TEMPLATE.m
- startup and diagnostics tests covering live broker PID plus foreign dashboard-port listener
- any wording changes needed so recovery stays explicit and single-path
- **Acceptance Criteria:**
- [ ] `--broker-console` and `--broker-daemon --web-ui` surface the foreign dashboard-port owner or both blockers when a live broker PID and foreign listener coexist
- [ ] `--doctor` does not hide the foreign listener behind a generic broker-without-dashboard diagnosis in the same mixed state
- [ ] Regression tests cover the mixed-state conflict and prevent reordering back to broker-only guidance
- [x] `--broker-console` and `--broker-daemon --web-ui` surface the foreign dashboard-port owner or both blockers when a live broker PID and foreign listener coexist
- [x] `--doctor` does not hide the foreign listener behind a generic broker-without-dashboard diagnosis in the same mixed state
- [x] Regression tests cover the mixed-state conflict and prevent reordering back to broker-only guidance

#### ⬜️ FU-P7-T3-2: Exclude broker-owned dashboard listeners from foreign port-conflict guidance
- **Description:** Refine the mixed broker/dashboard conflict classifier so it distinguishes the broker daemon's own dashboard listener from a foreign process on the same port. When degraded probes occur against a broker-owned listener, startup and diagnostics should keep users on broker-health guidance instead of reporting a foreign port owner.
- **Priority:** P1
- **Dependencies:** FU-P7-T3-1
- **Parallelizable:** yes
- **Outputs/Artifacts:**
- `src/mcpbridge_wrapper/__main__.py` mixed-state startup guidance that filters broker-owned listener PIDs out of foreign-conflict messaging
- `src/mcpbridge_wrapper/doctor.py` mixed-state diagnostic guidance with the same broker-owned listener exclusion
- `tests/unit/test_main.py` and `tests/unit/test_doctor.py` regression coverage for foreign-listener and broker-owned-listener mixed states
- **Acceptance Criteria:**
- [ ] Mixed-state foreign-port guidance triggers only when the dashboard listener PID differs from the running broker PID
- [ ] Broker-owned listeners with degraded dashboard probes do not tell users to stop an "existing listener" or use restart guidance meant for foreign ownership
- [ ] Regression tests cover both foreign-listener and broker-owned-listener mixed states in startup and doctor paths

#### ⬜️ P7-T4: Add direct local-status fallback for TUI when dashboard API is unavailable
- **Description:** Reduce TUI dependence on the Web UI API by letting it fall back to local broker state when the dashboard endpoint is unavailable. The TUI should still provide useful diagnostics from PID/socket/version files and any directly accessible broker status sources, while clearly indicating that live dashboard-backed controls are unavailable.
Expand Down
Loading