Branch: 048-tray-refetch-elimination | Date: 2026-05-08 | Spec: spec.md
Input: specs/048-tray-refetch-elimination/spec.md
Remove the 5 remaining /api/v1/servers refetch sites in the macOS tray. SSE-driven appState.servers (delivered by spec 047) is already authoritative; replace each refetch with an in-memory read or drop it entirely. Add one long-cadence (5 min) safety-net timer for missed-event resilience. Net result: tray-driven /api/v1/servers GETs at idle drop from ~8 / 60 s to ≤ 1 / 60 s.
Language/Version: Swift 5.9 (macOS 13+); Go 1.24 only for the verification harness, no Go changes in scope.
Primary Dependencies: SwiftUI/AppKit (existing), Combine (existing for the periodic timer pattern). No new deps.
Storage: None. Pure in-memory state.
Testing: XCTest (Swift). Live reproduction harness from spec 047 (/Applications/MCPProxy.app swap-in).
Target Platform: macOS 13+ (Personal edition).
Project Type: Native macOS tray app subtree of the multi-target repo.
Performance Goals: ≤ 1 /api/v1/servers GET per 60 s wall at idle (down from ~8). UI reactivity unchanged (≤ 50 ms from SSE event to visible update).
Constraints: No core / Go changes. No SSE contract change. No user-visible behavior change. Must remain backward-compatible with older cores (notify-only servers.changed fallback already handled by spec 047).
Scale/Scope: 5 call sites across 2 Swift files (CoreProcessManager.swift, MCPProxyApp.swift). One new Combine timer.
| Principle | Status | Notes |
|---|---|---|
| I. Performance at Scale | Reinforced | Drops residual idle CPU drag from the macOS client side; complements spec 047 server-side wins. |
| II. Actor-Based Concurrency | Aligned | All refactors stay within existing MainActor / Task patterns. No new shared mutable state. |
| III. Configuration-Driven Architecture | Aligned | The 5-minute safety-net cadence can be hard-coded; if user feedback ever asks for tuning, surface as a config key in a follow-up. |
| IV. Security by Default | No regression | No auth surface change. No new permissions. No data crossing trust boundaries. |
| V. TDD | Required | One failing XCTest per site change; see tasks.md. |
| VI. Documentation Hygiene | Aligned | Spec, plan, tasks, research, quickstart, verification all committed under specs/048-tray-refetch-elimination/. |
No violations. Complexity Tracking is empty.
specs/048-tray-refetch-elimination/
├── spec.md
├── plan.md ← this file
├── research.md ← Phase 0
├── data-model.md ← Phase 1
├── quickstart.md ← Phase 1
├── tasks.md ← Phase 2 (speckit.tasks)
└── verification/
├── http_log_idle.txt ← /api/v1/servers GETs over 60 s idle
└── report.md
(No contracts/ directory — this is a client-only refactor with no API change.)
native/macos/MCPProxy/MCPProxy/
├── Core/CoreProcessManager.swift ← sites 1, 2, 3 + the safety-net hook
└── MCPProxyApp.swift ← sites 4, 5
native/macos/MCPProxy/MCPProxyTests/
└── SSEHandlerTests.swift ← extend with 6 new tests (one per site + safety-net)
specs/048-tray-refetch-elimination/
└── verification/ ← post-fix http.log GET counts
Structure Decision: Pure Swift refactor; the file layout above is the entirety of the change set.
All decisions resolved during spec drafting on 2026-05-08. Key calls captured in research.md:
- 5-minute safety-net interval (chosen over 1 / 10 / 30 minutes).
- Approach for
refreshSecurityStatusDocker fallback (readappState.serverssynchronously). menuWillOpenstrategy (drop refetch entirely; rely on appState).- How to handle "tray just opened, appState empty" race (existing initial fetch on
connectcovers it).
data-model.md— declares only the new safety-net timer reference held on the app-level coordinator. No persistent storage.- No
contracts/directory — this PR doesn't change any API. quickstart.md— exact reproduction recipe for the live verification (build tray, swap into app bundle, launch, watchhttp.log).- Agent context update — runs at the end via
.specify/scripts/bash/update-agent-context.sh claude.
Generated by /speckit.tasks from this plan. Each site change is preceded by a failing XCTest.
See spec.md "Risks & Mitigations". No new risks identified during planning.
- Replacing
refreshActivity/refreshTokenMetrics/refreshSessionsperiodics (separate spec; needs SSE design for those domains). - Investigating whether
refreshSecurityStatus's Docker check could itself become SSE-driven (separate spec). - Adding a config key for the safety-net cadence (surface only if user feedback asks).
- Web UI changes (already covered by spec 047).
(empty — no Constitution gate violations)