Repository navigation
Fix two CLI telemetry signals that could not report what they claimed - #531
Conversation
The first day of live setup-funnel data surfaced two properties that carry no usable information. Both are measurement bugs, not collection bugs. has_existing_profile was true by construction. SetupCommand asked `LoadProfileConfig().Profiles.Count > 0`, but that method synthesizes a `default` entry whenever config.json is missing, so the count is >= 1 on a machine that has never run kcap. It reported true for 14/14 runs and could not distinguish first-time onboarding from a re-run — the split the funnel exists to measure. AppConfig.HasConfiguredProfile now defines "configured" as a profile carrying a server URL, which is what setup actually persists. is_ci missed most CI providers. Detection checked only CI and GITHUB_ACTIONS; Jenkins, TeamCity and Azure Pipelines export none of those and were counted as human machines. The inverse was broken too: a non-empty check treated the common opt-out CI=false as CI, tagging exactly the machines asking not to be. TelemetryEnvironment covers 17 providers plus the generic flag, and is pure over an injected environment so the table is testable without mutating the process env — the seam TelemetrySettings already uses. Also adds build_channel (release/prerelease/unknown), because is_ci does not cover the largest source of non-human devices in practice. Local dev loops and Aspire-spawned dev daemons run prerelease builds against throwaway KCAP_CONFIG_DIRs, minting a device id per run while legitimately reporting is_ci=false; they outnumbered real released-build devices 107 to 26 in the first day. The version string could express this, but only via a `cli_version NOT LIKE '%alpha%'` match every future insight must remember. McpTelemetryTests' property allowlist gains build_channel — that guard doing its job is why the new shared property had to be a deliberate decision. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
PR Summary by QodoFix CLI telemetry funnel signals and add build_channel property
AI Description
Diagram
High-Level Assessment
Files changed (8)
|
Code Review by Qodo🐞 Bugs (0) 📘 Rule violations (0) 📎 Requirement gaps (0)
Great, no issues found!Qodo reviewed your code and found no material issues that require reviewTip of the day💡 Did you know, you can group findings by type and pick your Finding display, from Minimal to Full |
Closes #530. Linear auto-imports the GitHub issue, so the imported AI-* issue links back to this PR.
Follow-up to #501 (CLI telemetry and the signup-funnel measurement gap), from reading the first day of live data.
Problem
Two setup-funnel properties cannot report what they claim.
has_existing_profileistrueby construction.SetupCommandaskedLoadProfileConfig().Profiles.Count > 0, but that method synthesizes adefaultentry wheneverconfig.jsonis missing (AppConfig.cs:273-274), so the count is >= 1 on a machine that has never run kcap. It reportedtruefor 14/14 recorded runs. The funnel therefore cannot separate first-time onboarding from a re-run — the split it exists to measure.is_cimisses most CI providers. Detection checked onlyCIandGITHUB_ACTIONS; Jenkins, TeamCity and Azure Pipelines export none of those and were counted as human machines. The inverse was broken too —!string.IsNullOrEmpty(...)treats the common opt-outCI=falseas CI, tagging exactly the machines asking not to be.Fix
AppConfig.HasConfiguredProfile(ProfileConfig)defines "configured" as a profile carrying a server URL — what setup actually persists — andSetupCommandcalls it instead of the count test.New
TelemetryEnvironmentcovers 17 CI providers plus the generic flag, presence-based per provider but value-aware forCI(soCI=falseopts out). It is pure over an injected environment, the same seamTelemetrySettingsuses, so the provider table is testable without mutating the process env.Also:
build_channelis_cidoes not cover the largest source of non-human devices in practice. Local dev loops and Aspire-spawned dev daemons run prerelease builds against throwawayKCAP_CONFIG_DIRs, minting a device id per run while legitimately reportingis_ci=false— they outnumbered real released-build devices 107 to 26 in the first day. Every event now carriesbuild_channel(release/prerelease/unknown), so excluding them is a normal property filter rather than acli_version NOT LIKE '%alpha%'string match every future insight has to remember.McpTelemetryTests' property allowlist gainsbuild_channel— that guard doing its job is precisely why a new shared property has to be a deliberate decision.Tests
TDD throughout: tests written first, watched fail for the right reason, then implemented.
AppConfigHasConfiguredProfileTests(5): the synthesized-defaultcase that caused the bug, blank/whitespace server URL, a configured non-active profile, no profiles at all.TelemetryEnvironmentTests(18 + 8): one case per provider variable,CI=false/False/0opting out, blank values, provider var set to"false"still counting, andbuild_channelincluding the trap where build metadata itself contains a hyphen (0.11.17+feature-branch.1is a release).Full unit suite: 57 pre-existing failures on both this branch and a clean
origin/mainbaseline (stashed and re-ran to confirm) — the known-flaky PTY/timer/Codex-TOML classes. The one test this change broke,No_argument_data_is_carried, is the property allowlist and is fixed here. Two timing-sensitive tests differed run-to-run; both pass in isolation and touch neither telemetry nor config.AOT publish clean — no IL2026/IL3050.
Verified against the real AOT binary:
has_existing_profile: truefalsetruetrueTEAMCITY_VERSION=2024.1is_ci: falsetrueCI=falseis_ci: truefalseNotes
No README change: no CLI surface changed, and
build_channelis derived from the already-sentcli_version, so the privacy statement is unaffected. The event catalog indocs/superpowers/specs/2026-08-08-cli-telemetry-signup-funnel-design.mdis updated with both corrections.Existing
has_existing_profiledata (v0.11.16 through v0.11.18) should be treated as unusable rather than as evidence that everyone re-runs setup.🤖 Generated with Claude Code