Skip to content

fix: telemetry opt-out shown as effective state; macOS Settings names the connected core (Spec fix-usertest-telemetry-macos) - #1471

Merged
github-actions[bot] merged 13 commits into
mainfrom
fix-usertest-telemetry-macos-settings
Oct 3, 2026
Merged

github-actions[bot] merged 13 commits into
mainfrom
fix-usertest-telemetry-macos-settings

Conversation

@Dumbris

@Dumbris Dumbris commented Oct 2, 2026

Copy link
Copy Markdown
Member

Summary

Two medium findings from the codex first-run user test, fixed on every surface that shows them. Both are on the Spec 109 side of the ownership rule (onboarding and the telemetry notice, Settings, macOS Settings); nothing here touches Spec 108 surfaces.

F-03: the notice and Settings now show the effective telemetry state

With MCPPROXY_TELEMETRY=false (or DO_NOT_TRACK, CI) the core sends nothing, but the Web wizard and Home banner still said "MCPProxy sends anonymous usage statistics", the macOS first-run welcome did the same, and Settings showed the stored telemetry.enabled: true as on.

  • REST and Go. GET /api/v1/status gains telemetry: {enabled, source, disabled_by}. source is env, config or default. disabled_by appears only for env and reuses the existing reasons. The block is withheld from scoped callers (agent tokens), like activation. It is resolved from the running config by the new telemetry.ResolveEffectiveState, which always agrees with EffectiveTelemetryEnabled. GET /api/v1/config is unchanged: it stays the stored, round-trippable document.
  • Web. The wizard's Verify step and the Home banner show one of three modes. Standard notice when telemetry is on or the state is unknown. "Anonymous usage telemetry is off — disabled by MCPPROXY_TELEMETRY=false in the environment. Nothing is sent." for an environment opt-out, with no "Manage in Settings" link. Nothing when the user turned telemetry off in their own config. Settings → General shows the telemetry toggle off and disabled with the reason and how to unlock it, and a locked setting never counts as changed or gets saved. The wizard notice is now the banner component's inline variant, so the copy lives in one place. A failed status fetch (for example a 401 before the API key is stored) is retried instead of cached.
  • macOS. The first-run welcome reads the app's own environment, because it is modal at launch before any core answers; a tray-spawned core inherits that environment. Settings locks the telemetry toggle off with the same reason, using the same copy as the Web UI.
  • Proof that nothing is sent. A new test drives the real service with a valid release version against a counting server under MCPPROXY_TELEMETRY=false, FALSE, DO_NOT_TRACK=1 and CI=true through Start, the shutdown flush and the opt-out beacon, and expects zero requests. A control run with no environment variable must reach the server, so the test cannot pass vacuously. Neutering both env gates makes it fail.

F-10: macOS Settings names the core it is editing

The reported "wrong listen address and telemetry state" came from the dev app being attached to the user's real v0.69.0 core: the tray ignores HOME, and open drops shell variables. Settings was showing the connected core's values correctly; nothing told the tester which core that was. Settings → Security now shows, under Listen address, "Connected core vX is listening on

." with "The saved address Y takes effect after a restart." when it differs, and a config without listen shows the running address instead of the placeholder without becoming a pending change. docs/development/macos-tray.md documents the dev-rig trap.

Audit findings fixed

F-03 (medium) and F-10 (medium) of the codex first-run user test.

Spec

specs/109-ux-navigation-consistency: Phase 17 (T172 to T177), US7-7 and US7-8, FR-044a and FR-044b, parity rows 28a and 29a, research D37, quickstart recipe fix-usertest-telemetry-macos, plan row. The spec-count assertions in the traceability and parity tests move from 43 to 45 scenarios and from 32 to 34 matrix rows. Docs updated: the REST status section (status.telemetry), the telemetry feature page (how the UI shows an environment opt-out) and the macOS tray development page.

Tests added

  • Go: internal/telemetry/effective_state_test.go (shared fixture internal/telemetry/testdata/effective_state_cases.json), internal/telemetry/env_gate_send_test.go, internal/httpapi/status_telemetry_test.go.
  • Web: telemetry-state.spec.ts (same fixture), settings-telemetry-env-lock.spec.ts, extended telemetry-banner-wizard.spec.ts.
  • macOS: TelemetryNoticeTests.swift (same fixture), SettingsEffectiveStateTests.swift.
  • The CLI parity walk now resolves mcpproxy telemetry status.

No new dependencies. oas/swagger.yaml changes by the one description line.

Follow-ups (not in this PR)

config.IsTelemetryEnabled compares the environment value strictly to "false" while IsDisabledByEnv trims and case-folds; ConvertConfigToContract materialises an environment-forced false into the config document when the stored value is nil; the tray could warn when HOME differs but MCPPROXY_HOME is unset; the Web listen field could show the running address when it differs from the configured one.

Resolves env opt-out vs config vs default into one EffectiveState
{enabled, source, disabled_by} so UIs can show the real state. Adds a
send-nothing proof for MCPPROXY_TELEMETRY, DO_NOT_TRACK and CI.
The wizard Verify step and Home banner say telemetry is off and why when an
environment variable disables it, stay silent when the user's own config
disables it, and Settings locks the toggle off with the reason.
…e in Settings

The first-run welcome says telemetry is off and why when the app's
environment disables it. Settings locks the telemetry toggle off with the
reason, names the connected core and its running listen address, adopts that
address when the config omits listen, and notes a pending restart.
…gs user-test fixes

Spec, tasks, parity rows 28a and 29a, acceptance scenarios US7-7 and US7-8,
research D37, quickstart recipe, REST, telemetry and macOS tray docs.
A 401 before the API key is stored no longer starts the 30 s reuse window.
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

Deploying mcpproxy-docs with  Cloudflare Pages  Cloudflare Pages

Latest commit: 4f6aaa8
Status: ✅  Deploy successful!
Preview URL: https://205e27db.mcpproxy-docs.pages.dev
Branch Preview URL: https://fix-usertest-telemetry-macos.mcpproxy-docs.pages.dev

View logs

F4.1 (macOS): the Settings listen note cached the running address from the
first load only. ConfigStore now re-reads /api/v1/status when the tray lands on
a new core connection and whenever a Settings tab appears, so the note follows
a restarted core (FR-044b).

F3.1 (web): the Raw JSON Apply path posted the whole document without checking
field locks, so an env-locked telemetry.enabled could be persisted. Apply now
refuses a document that changes a locked key (FR-044a). The macOS Raw tab is
read-only, so it has no equivalent path.
@codecov-commenter

codecov-commenter commented Oct 2, 2026 •

Copy link
Copy Markdown

⚠️ Please install the 'codecov app svg image' to ensure uploads and comments are reliably processed by Codecov.

Codecov Report

❌ Patch coverage is 93.75000% with 2 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
internal/httpapi/config_funnel.go 87.50% 1 Missing and 1 partial ⚠️

📢 Thoughts on this report? Let us know!

@github-actions

github-actions Bot commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

📦 Build Artifacts

Workflow Run: View Run
Branch: fix-usertest-telemetry-macos-settings

Available Artifacts

  • archive-darwin-amd64 (31 MB)
  • archive-darwin-arm64 (28 MB)
  • archive-linux-amd64 (19 MB)
  • archive-linux-arm64 (17 MB)
  • archive-windows-amd64 (31 MB)
  • archive-windows-arm64 (27 MB)
  • frontend-dist-pr (0 MB)
  • installer-dmg-darwin-amd64 (27 MB)
  • installer-dmg-darwin-arm64 (24 MB)
  • smart-mcp-proxymcpproxy-goAB5V25.dockerbuild (0 MB)

How to Download

Option 1: GitHub Web UI (easiest)

  1. Go to the workflow run page linked above
  2. Scroll to the bottom "Artifacts" section
  3. Click on the artifact you want to download

Option 2: GitHub CLI

gh run download 37080784917 --repo smart-mcp-proxy/mcpproxy-go

Note: Artifacts expire in 14 days.

macOS Settings: an adopted listen address follows the running core on every
status refresh while the config omits listen and the field is unedited, and
the pending-restart note reads the config file, never the adopted mirror
(F1.1, XCTest covers a running-address change and an edited field).

Web: lockedKeysChanged matches keys case-insensitively with last-key-wins,
as the backend decodes Raw JSON (F1.2); Raw JSON Apply refreshes the
effective telemetry state (F3.2). Tests fail without the fixes.

Deferred: server-side enforcement of the telemetry lock in the apply handler
(F1.2 preferred variant). The stored value is a legitimate setting that the
environment overrides at runtime; rejecting it would break scripted edits.

QA.mac: live macOS verification was not done in this stage; the screen was
unlocked here but the live-QA stage owns it. Not faked.
Web: lockedKeysChanged no longer assumes document-order last-key-wins. The
backend round-trips Raw JSON through a map (UnmaskLiveConfigDocument marshals
sorted keys), so among case-variant duplicates the byte-order winner decides,
and same-named objects merge. Every case-variant reading of a locked key is
now collected and the document is refused when any differs from the stored
value (fail toward refusal). The new test fails on the previous code: a
document whose document-order last key matched the stored value but whose
sorted winner turned telemetry off slipped through.

Deferred: server-side enforcement of the telemetry lock in the apply handler.
The stored value is a legitimate setting that the environment overrides at
runtime; rejecting it would break scripted edits.
…tch (Spec 109 FR-044a)

Merge origin/main and add server-side enforcement of the telemetry.enabled
lock the Web and macOS UIs show while DO_NOT_TRACK, CI or
MCPPROXY_TELEMETRY=false forces telemetry off. POST /config/apply and
PATCH /config answer 422 and write nothing when the typed result would change
the value (miscased keys included); unchanged values, including the GET-then-POST
round trip of an unset setting, pass.
Renumber this PR's Spec 109 tasks to T205-T210 (main owns T199-T204 from
fix-usertest-web) and research D38 to D42; Phase 20; task count 246.
Keep main's FR-043 text next to FR-044/FR-044a/FR-044b. Regenerate
oas/docs.go, oas/swagger.yaml and ROADMAP.md.

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved (Model B): Paperclip review verdicts = ACCEPT and qa-gate green at this head SHA. Arming auto-merge; GitHub merges when all required checks pass.

@github-actions
github-actions Bot merged commit 8e5ccd3 into main Oct 3, 2026
61 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants