From bc67a6fbdcbd6e4c00f6dae716b3eac66ad87559 Mon Sep 17 00:00:00 2001 From: MatiasFernandez <7898112+MatiasFernandez@users.noreply.github.com> Date: Wed, 7 Oct 2026 15:39:32 -0300 Subject: [PATCH 1/3] Stamp the host editor on every telemetry event The VS Code version distribution is meant to answer whether engines.vscode can be raised, but forks (Cursor, Windsurf) report their base VS Code version in common.vscodeversion and no common.* property names the app. A group stuck on an old 1.9x could be one fork's whole user base. Every event now carries appUriScheme (vscode.env.uriScheme) and appName (vscode.env.appName), the names the gemdb-azure workbooks expect. They are raw strings, so a new fork shows up without a release. In a remote window they name the client's editor: VS Code's remoteExtensionHost.ts hands the remote host the client's product values. That is from reading the source, not a remote run; rows with common.remotename set will confirm it. docs/telemetry.md lists both, and USAGE_DATA.md names them, since a new common property changes what is collected. Co-Authored-By: Claude Opus 5.5 --- USAGE_DATA.md | 8 ++++++-- docs/telemetry.md | 2 ++ src/__mocks__/vscode.ts | 2 ++ src/__tests__/telemetryDocs.test.ts | 19 +++++++++++++++++++ src/telemetry.ts | 13 +++++++++++-- 5 files changed, 40 insertions(+), 4 deletions(-) diff --git a/USAGE_DATA.md b/USAGE_DATA.md index f56c1a4..aef34ea 100644 --- a/USAGE_DATA.md +++ b/USAGE_DATA.md @@ -24,7 +24,7 @@ VS Code automatically attaches its own common properties to every event: `common.isnewappinstall`, `common.sqmid`, `common.devDeviceId`, `common.telemetryclientversion`. -GemDB adds three properties of its own to every event: +GemDB adds five properties of its own to every event: - `extensionMode`, which is `production` for an installed extension and `development` or `test` when a GemDB developer is running it from source. @@ -34,6 +34,10 @@ GemDB adds three properties of its own to every event: - `firstSeen` — the ordinary case: GemDB recorded this as a new install. - `reinstall` — GemDB's own files were reinstalled while the database survived. +- `appName` and `appUriScheme`, which name the editor GemDB runs in, as its + name and its URL scheme (e.g. "Cursor" and `cursor`). Editors built on VS + Code report the VS Code version they are based on, so this is what tells + them apart. `common.vscodemachineid` is a pseudonymous identifier VS Code generates per installation. It is not tied to your name or email, but under GDPR it counts @@ -98,5 +102,5 @@ delete the matching records. ## Changes -Last updated: 2026-10-05. Changes to this notice will be published in this +Last updated: 2026-10-07. Changes to this notice will be published in this repository. diff --git a/docs/telemetry.md b/docs/telemetry.md index 0ac93e9..5d1ff62 100644 --- a/docs/telemetry.md +++ b/docs/telemetry.md @@ -29,6 +29,8 @@ take the earliest event of that kind for its `common.vscodemachineid`. | `extensionMode` | `production`, `development`, `test` | Only `production` is a real user. Filter out the others in every report. | | `installDay` | a date, e.g. `2026-09-15` | Roughly when GemDB was first used on this machine. | | `installDaySource` | `firstSeen`, `reinstall` | `reinstall`: GemDB was reinstalled but the user's database was still there. | +| `appUriScheme` | e.g. `vscode`, `vscode-insiders`, `cursor`, `windsurf` | Which editor GemDB runs in. Editors built on VS Code report the VS Code version they are based on in `common.vscodeversion`, so group by this before reading versions. It is the user's own editor, even in a Remote-SSH, WSL or container window. Missing on events from 1.5.4 and earlier. | +| `appName` | e.g. `Visual Studio Code`, `Cursor` | The same editor, by its display name. Missing on events from 1.5.4 and earlier. | VS Code adds its own `common.*` properties too (machine id, OS, VS Code version, extension version). [USAGE_DATA.md](../USAGE_DATA.md) lists them. diff --git a/src/__mocks__/vscode.ts b/src/__mocks__/vscode.ts index eb4bfd8..d88502f 100644 --- a/src/__mocks__/vscode.ts +++ b/src/__mocks__/vscode.ts @@ -112,6 +112,8 @@ const FAKE_COMMON_PROPERTIES: Record = { 'common.fake': 'yes' }; /** Two-line insurance: nothing here reads `env` today, but `activate()` does. */ export const env = { machineId: 'fake-machine-id', + appName: 'Visual Studio Code', + uriScheme: 'vscode', remoteName: undefined as string | undefined, clipboard: { writeText: (text: string): Promise => { diff --git a/src/__tests__/telemetryDocs.test.ts b/src/__tests__/telemetryDocs.test.ts index 05080d3..7a8899f 100644 --- a/src/__tests__/telemetryDocs.test.ts +++ b/src/__tests__/telemetryDocs.test.ts @@ -101,3 +101,22 @@ describe('docs/telemetry.md', () => { expect(undocumented, `document these in ${DOC_PATH}`).toEqual([]); }); }); + +// The same `beforeAll` is what makes this assertion mean "every event": it +// drives all seven `report*` functions, and the suite above fails if that +// list ever falls behind `EVENT`. Asserting it off one event — activation, +// say — would pass for a `report*` that built its own properties object +// instead of going through `send`. +describe('every event', () => { + it('names the editor GemDB runs in', () => { + // Forks report their base VS Code version, so these are what tells a + // Cursor user from a VS Code user on an old release. + expect(__telemetry.length).toBeGreaterThan(0); + for (const event of __telemetry) { + expect(event.properties, event.name).toMatchObject({ + appUriScheme: 'vscode', + appName: 'Visual Studio Code', + }); + } + }); +}); diff --git a/src/telemetry.ts b/src/telemetry.ts index 05cbc48..ef94298 100644 --- a/src/telemetry.ts +++ b/src/telemetry.ts @@ -82,8 +82,15 @@ let reporter: TelemetryReporter | undefined; * Properties stamped on every event, established once at activation. * * `extensionMode`, plus `installDay` and `installDaySource` (see - * `resolveInstallDay`). VS Code's own `common.*` properties are mixed in by - * the extension host and are not repeated here. + * `resolveInstallDay`), plus `appUriScheme` and `appName`, which name the + * editor GemDB runs in. Forks (Cursor, Windsurf) report their *base* + * VS Code version in `common.vscodeversion`, and no `common.*` property names + * the app, so without these a fork's users read as VS Code users stuck on an + * old release. Sent as raw strings, not mapped to a list, so a new fork shows + * up without a release. In a remote window they are still the client's app: + * the remote extension host is handed the client's product values (read from + * VS Code's `remoteExtensionHost.ts`, not measured). VS Code's own `common.*` + * properties are mixed in by the extension host and are not repeated here. */ let baseProperties: Record = {}; @@ -217,6 +224,8 @@ export function initTelemetry(context: vscode.ExtensionContext, databaseExists: extensionMode: extensionModeName(context.extensionMode), installDay, installDaySource, + appUriScheme: vscode.env.uriScheme, + appName: vscode.env.appName, }; // Disposal flushes queued events, so the subscription is load-bearing. reporter = new TelemetryReporter(CONNECTION_STRING); From df4d1e31ae43d37af2bd5e8d001b1eb56e427b58 Mon Sep 17 00:00:00 2001 From: MatiasFernandez <7898112+MatiasFernandez@users.noreply.github.com> Date: Wed, 7 Oct 2026 15:39:43 -0300 Subject: [PATCH 2/3] Note the editor property in the changelog Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2838630..91f7977 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -68,6 +68,8 @@ this one does not look. `gemdb.admin.garbage_collect()`, `gemdb.admin.backup()` and `gemdb.sessions.all()` now raise a security error; **Collect Garbage Now** and **Stop a Database Session…** cover the first and last. +- **Usage data now records which editor GemDB runs in** (VS Code, Cursor, …), so a + fork's users are not mistaken for VS Code users on an old release. `USAGE_DATA.md` lists it. ### Fixed From b5108e688df12443b228674810c71b7fb23bf64f Mon Sep 17 00:00:00 2001 From: MatiasFernandez <7898112+MatiasFernandez@users.noreply.github.com> Date: Wed, 7 Oct 2026 16:33:57 -0300 Subject: [PATCH 3/3] Say what appName and appUriScheme read in a remote window The remote-window behaviour was marked as read but not measured. It is settled from VS Code's source at microsoft/vscode e8f54c79d5c46126f427cccd37cceb531b0dcdf6: - src/vs/workbench/api/common/extHost.api.impl.ts L430/L433: env.appName and env.uriScheme return initData.environment.appName/appUriScheme. - src/vs/workbench/services/extensions/common/remoteExtensionHost.ts L219/L221: the desktop builds that init data for a remote extension host from its own productService (nameLong, urlProtocol) and sends it at L159; only paths and the pid come from the server. - src/vs/workbench/api/node/extensionHostProcess.ts: the server parses the init data and never sets appName or appUriScheme. GemDB declares no extensionKind and has a main entry, so it defaults to workspace and runs on the remote side in a remote window; this is what makes it report the desktop app there. docs/telemetry.md now says so for someone reading the data, including common.remotename and the browser case. Co-Authored-By: Claude Opus 5.5 --- docs/telemetry.md | 8 +++++++- src/telemetry.ts | 7 ++++--- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/docs/telemetry.md b/docs/telemetry.md index 5d1ff62..feaca80 100644 --- a/docs/telemetry.md +++ b/docs/telemetry.md @@ -29,9 +29,15 @@ take the earliest event of that kind for its `common.vscodemachineid`. | `extensionMode` | `production`, `development`, `test` | Only `production` is a real user. Filter out the others in every report. | | `installDay` | a date, e.g. `2026-09-15` | Roughly when GemDB was first used on this machine. | | `installDaySource` | `firstSeen`, `reinstall` | `reinstall`: GemDB was reinstalled but the user's database was still there. | -| `appUriScheme` | e.g. `vscode`, `vscode-insiders`, `cursor`, `windsurf` | Which editor GemDB runs in. Editors built on VS Code report the VS Code version they are based on in `common.vscodeversion`, so group by this before reading versions. It is the user's own editor, even in a Remote-SSH, WSL or container window. Missing on events from 1.5.4 and earlier. | +| `appUriScheme` | e.g. `vscode`, `vscode-insiders`, `cursor`, `windsurf` | Which editor GemDB runs in. Editors built on VS Code report the VS Code version they are based on in `common.vscodeversion`, so group by this before reading versions. Missing on events from 1.5.4 and earlier. | | `appName` | e.g. `Visual Studio Code`, `Cursor` | The same editor, by its display name. Missing on events from 1.5.4 and earlier. | +In a remote window (Dev Container, SSH, WSL), `appUriScheme` and `appName` +describe the editor on the user's desktop, not the remote machine, so a remote +window reports the same values as a local one. `common.remotename` says whether +the window was remote. When the editor runs in a web browser, as with +`code serve-web`, they describe that web version of the editor instead. + VS Code adds its own `common.*` properties too (machine id, OS, VS Code version, extension version). [USAGE_DATA.md](../USAGE_DATA.md) lists them. diff --git a/src/telemetry.ts b/src/telemetry.ts index ef94298..3b95140 100644 --- a/src/telemetry.ts +++ b/src/telemetry.ts @@ -87,9 +87,10 @@ let reporter: TelemetryReporter | undefined; * VS Code version in `common.vscodeversion`, and no `common.*` property names * the app, so without these a fork's users read as VS Code users stuck on an * old release. Sent as raw strings, not mapped to a list, so a new fork shows - * up without a release. In a remote window they are still the client's app: - * the remote extension host is handed the client's product values (read from - * VS Code's `remoteExtensionHost.ts`, not measured). VS Code's own `common.*` + * up without a release. In a remote window they are still the desktop app's: + * the desktop builds the remote extension host's init data from its own + * product info (`remoteExtensionHost.ts`) and the server never overrides + * `appName` or `appUriScheme`. VS Code's own `common.*` * properties are mixed in by the extension host and are not repeated here. */ let baseProperties: Record = {};