feat(telemetry): opt-in anonymous usage sharing [roadmap:v0.10.6] - #75
Merged
Conversation
…v0.10.6] Implements rac/roadmaps/v0.10.x-guide/v0.10.6-anonymous-usage-sharing.md (contract): consented, anonymous, pinned daily ping; consent surfaces; honesty-surface rewrite; contract battery with a network isolation rule.
Pins the phase-2 telemetry shape: opt-in twice over, pinned payload (install id, version, active-repo count — never content or paths), PostHog as a swappable third-party sink, empty-key kill switch, and a one-module network surface. Amends ADR-040's no-network-code clause; the amendment is noted in ADR-040's relationship section.
Implements rac/roadmaps/v0.10.x-guide/v0.10.6-anonymous-usage-sharing.md. Adds rac.consent: corruption-tolerant consent record under XDG config, random install id minted at opt-in and preserved across toggles, local-only repo-digest salt, persisted declines for ask-once, and the PostHog constants with the empty-key kill switch.
Implements rac/roadmaps/v0.10.x-guide/v0.10.6-anonymous-usage-sharing.md. Adds rac.mcp.ping — the only network code in RAC: pinned payload, 24-hour marker dedupe, salted active-repo digests (count only crosses the wire), 3s timeout, failures swallowed, no retries, daemon thread. run_server starts it only with recorded consent and a configured key, announcing on stderr; tool responses stay byte-identical.
…:v0.10.6] Implements rac/roadmaps/v0.10.x-guide/v0.10.6-anonymous-usage-sharing.md. Adds rac telemetry [on|off|status] and the CLI's first interactive prompt: one honest usage-sharing question after a successful rac init, TTY-gated, default No, never with --json, persisted either way so it is asked at most once per machine.
…v0.10.6] Implements rac/roadmaps/v0.10.x-guide/v0.10.6-anonymous-usage-sharing.md. Adds: - ping battery: pinned payload key sets, wire capture, swallowed failures with one attempt per day, 24h dedupe, salted active-repo digests, thread gating, server-hook notices - init prompt battery: TTY gating, default No, ask-once, --json purity - isolation rule: network client modules importable only by rac.mcp.ping - CI matrix entry for the new battery
…0.10.6] Implements rac/roadmaps/v0.10.x-guide/v0.10.6-anonymous-usage-sharing.md. Rewrites every surface that claimed nothing leaves the machine: the README trust bullet becomes opt-in twice over, docs/mcp.md shows the entire pinned payload verbatim, docs/cli.md documents rac telemetry and the one-time init question, and the changelog names the kill switch.
…ap:v0.10.6] An autouse fixture points XDG_CONFIG_HOME and XDG_STATE_HOME at temp directories suite-wide, so no test can read a developer's consent record or real state — a precondition for carrying a live PostHog key in source.
…:v0.10.6] Fills the live project write key and EU capture endpoint, and aligns the pinned payload with PostHog's documented contract: distinct_id rides inside properties, and $process_person_profile: false marks the event anonymous so the sink creates no person profile. ADR-041, the roadmap contract, the docs example, and the battery are re-pinned in the same change.
…0.10.6] v0.10.5 was claimed by the bundled-skills milestone merged on main while this branch was in flight; the anonymous usage-sharing roadmap and every reference to it move to v0.10.6. Also reconciles the CLI subcommand count (telemetry and skill both landed). No behavior change.
tcballard
force-pushed
the
claude/mcp-telemetry-opt-in-0nucfg
branch
from
June 12, 2026 20:50
80af15d to
b470534
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Implements
rac/roadmaps/v0.10.x-guide/v0.10.6-anonymous-usage-sharing.md.Adds:
rac mcpsends at most one daily ping to PostHog (EU) — a random install id, the RAC version, and a 30-day active-repo count; never paths, queries, arguments, or repository contentrac telemetry on|off|status, plus the CLI's first interactive prompt — one honest question after a successfulrac init, TTY-gated, default No, asked at most once per machineNote: this milestone was authored as v0.10.5 and renumbered to v0.10.6 after the bundled-skills milestone (#73) claimed v0.10.5 on main mid-flight; the branch is rebased onto that merge.
Roadmap / ADR Trace
Roadmap:
rac/roadmaps/v0.10.x-guide/v0.10.6-anonymous-usage-sharing.mdRelevant ADRs:
rac/decisions/adr-041-anonymous-usage-ping.md(new — consented daily ping, pinned payload, PostHog as third-party sink)rac/decisions/adr-040-guide-local-telemetry.md(amended: one relationship line notes ADR-041 narrows its "RAC contains no network code" clause; local recording and the share-URL flow are unchanged)rac/decisions/adr-032-guide-stateless-reads.md(untouched — the ping runs outside the request/response contract; tool responses stay byte-identical)rac/decisions/adr-035-byo-ai-credentials.md(honored — the dependency is optional by consent and inert without a key)Scope
Included
src/rac/consent.py(stdlib-only, outsiderac.mcpsorac init/rac telemetrynever import the MCP SDK): corruption-tolerant consent record at~/.config/rac/telemetry.json; randomsecrets.token_hex(16)install id minted at opt-in and preserved across off→on toggles; a local-only salt for repo digests; persisted declines so the init question is ask-once. The PostHog constants live here (EU capture endpoint plus the public project write key) — blanking the key is a kill switch that disables all sendingsrc/rac/mcp/ping.py— RAC's entire network surface, the only module permitted to importurllib.request(new isolation rule): daemon thread started byrun_server, at most one attempt per 24h via a local marker, 3-second timeout, every failure swallowed, no retries, payload pinned in full~/.local/state/rac/active-repos.json, pruned to 30 days; only the count crosses the wire and the salt never does_maybe_start_sharinginrun_server— independent of the local--telemetryflag, announced on stderr (stdout stays protocol-only), silent without consentXDG_CONFIG_HOME/XDG_STATE_HOMEfor every test, committed before the live key landed — no test run can find a developer's consent record and phone homeExcluded
rac mcp-stats, v0.10.4)Product / Architecture Decisions
distinct_idrides insideproperties, and"$process_person_profile": falsemarks events anonymous so PostHog creates no person profiles — cheaper, and a sink-side enforcement of the anonymity posturerac.mcpbecause importing that package pays the eager MCP SDK import; the ping module imports them fromrac.consent--telemetry: each is its own opt-in; consenting without serving sends nothing, and recording locally never implies sharingrac initprompt is deliberately the CLI's first interactive prompt; the ADR records the precedent and its gates (TTY both ends, no--json, no existing record, EOF/empty means No)User-Facing Contract
CLI
Human Output
rac init(TTY, no prior answer):Share anonymous usage to help shape Lore? [y/N]— default No, either answer persistedrac telemetry status: sharing on/off, install id, consent timestamp and file path, what is shared daily, and an explicit note when no endpoint key is configured (in that state nothing is sent)rac mcpwith consent on announces on stderr: at most one daily ping, what it contains, and how to disable — sharing is never silentWire Contract (the entire transmission)
{ "api_key": "phc_... (the public project write key)", "event": "lore-daily-ping", "timestamp": "2026-06-12T20:37:04Z", "properties": { "distinct_id": "cd03eed3dd98b784834485e27a436914 (random install id)", "$process_person_profile": false, "schema_version": "1", "rac_version": "0.10.6", "active_repos": 2 } }Exit Codes
0: telemetry consent shown or changed (on/off/status, including when no key is configured); init behavior unchanged2: invalid telemetry action (argparse choices)Verification
Ran
Manual end-to-end against a local HTTP sink standing in for PostHog:
rac telemetry on→rac mcpstartup produced the stderr notice and exactly one POST whose body matched the pinned payload byte-for-byte (real key,distinct_idand$process_person_profile: falseinsideproperties,active_repos: 1); a second startup within 24h sent nothing; afterrac telemetry offthe restart was fully silent; the state file contained only a salted digest, no path.Covered
--json;yrecords consent; empty/n/EOF persist a decline; a second init never asks; non-TTY never reachesinput()urlopenswallowed with exactly one attempt; marker written after failure (no retry within 24h);should_pinghonors fresh/stale/corrupt markersdaemon=True;_maybe_start_sharingsilent without consent, announces with key, names the kill switch without oneurllib.request,http.client) importable only byrac/mcp/ping.py; all existing batteries pass unchanged, including the v0.10.4 byte-identical-response guardsReview Path
rac/decisions/adr-041-anonymous-usage-ping.mdand the roadmap — the contract, including the ADR-040 amendmentsrc/rac/consent.py— consent record, ids, kill-switch constantssrc/rac/mcp/ping.py— the one network module: payload, dedupe, failure posturesrc/rac/mcp/server.py(_maybe_start_sharing) andsrc/rac/cli.py(cmd_telemetry, the init prompt)tests/test_mcp_ping.py,tests/test_init.py,tests/test_mcp_isolation.py,tests/conftest.py— the battery, prompt gates, network rule, hermeticityREADME.md,docs/mcp.md,docs/cli.md,CHANGELOG.md— the honesty rewriteNotes For Reviewer
src/rac/consent.py, so once released, consenting installs actually transmit. The key is a public write-only key (standard for client telemetry); blanking it disables all sending. The hermeticity fixture was committed before the key so no point in history has a live key with non-hermetic tests.rac mcp. Retention = distinctdistinct_idper day on thelore-daily-pingevent.Implementation Process
Implemented with AI assistance under the roadmap contract.
Final scope, review, and acceptance decisions were made by the maintainer.