Independent exact-head verification of PR #238 · 009d9af into c0fa884
009d9af22b1c23214635eb7eb5af1fc49f8f1642 and its PR base is c0fa884ccfd1de4d21e98f83b5f88abd56f1fb9b.T-1824 and T-1821 are retained under Unreleased.claude-review job completed successfully for this exact SHA, and GraphQL reports a clean, mergeable PR with zero unresolved threads.Ready / LGTM
PASS. The required P1 is fixed: the exact merge diff preserves the already-merged T-1824 Unreleased entry and adds the final T-1821 entry immediately after it. No actionable findings remain.
Shown verbatim — the markdown the author wrote, unmodified.
## Summary - Route committed MCP Settings port edits, including focus loss and view teardown, through the existing desired-state listener lifecycle. - Keep setup instructions aligned with `MCPServer.activePort`; cancel pending edits when MCP is disabled. - Add testable coordinator state and a live loopback focus-loss regression. ## Root cause The Settings port binding persisted independently, while listener restart was limited to `TextField.onSubmit`, leaving a focus-loss commit advertised but not bound. ## Validation - `make test-quick` ✅ - `make lint` ✅ - `make test` ⚠️ two attempts timed out; the second reported unrelated UI failures in `testClearAll`, `testEditViewPreservesTaskMilestone`, and `testDataMaintenanceGoldenPath` before timeout. T-1821 is macOS-only. See `specs/bugfixes/mcp-port-listener-alignment/report.md`.
ab66999 T-1821: Add MCP port alignment investigation 75aba9a T-1821: Align MCP port settings with listener 16daedf T-1821: Remove unrelated changelog entry 009d9af T-1821: Preserve merged changelog entries Changing the MCP port and leaving the field without pressing Return now updates the running server too. The connection command continues to show the port that is actually listening, rather than a saved value that has not yet taken effect.
The Settings view forwards committed port changes to a small debouncing coordinator. That coordinator deliberately owns only pending UI work; it delegates all listener start, stop, replacement, and invalid-port behavior to MCPServer's existing serialized desired-state lifecycle.
MCPPortChangeState remains a pure nonisolated value type for deterministic deduplication and cancellation tests, while the MainActor coordinator serializes debounce/flush behavior with the UI. Clearing pending state synchronously before stop makes a late cancelled debounce consume no port. The loopback regression proves the actual focus-loss path replaces the socket binding.
CHANGELOG.md
Why it matters. Prevents merging PR #238 from dropping the already-merged T-1824 release note.
What to look at. Unreleased entries
Transit/Transit/MCP/MCPPortChangeCoordinator.swift
Why it matters. Fixes the focus-loss and Settings-teardown path that previously persisted a port without moving the listener.
What to look at. MCPPortChangeState and MCPPortChangeCoordinator
Transit/Transit/Views/Settings/SettingsView.swift
Why it matters. Ensures the displayed setup command follows the active listener and the actual focus-loss path is covered.
What to look at. onChange, cancellation/flush, activePort presentation, and focusLossPortCommitReplacesLiveListenerWithoutSubmit
The coordinator forwards to MCPServer.start(port:) rather than duplicating start/stop/rebind logic. This preserves the established desired-state coordinator as the single listener owner.
The setup command reads activePort, so it cannot direct clients to a replacement that has not become live.
Click to expand.
(no diff provided)
(no diff provided)
(no diff provided)
(no diff provided)
(no diff provided)
(no diff provided)
(no diff provided)