Skip to content

Commit a91fbe4

Browse files
anandgupta42claude
andcommitted
refactor: cover client_connected server wiring and drop the redundant client field
Addresses the independent review of the IDE-funnel telemetry change. - Drop the `client` property from `startup_ready` and `client_connected`. `toAppInsightsEnvelopes` already stamps `source: Flag.ALTIMATE_CLI_CLIENT` onto every event that has no `source` of its own, and neither of these events does, so `client` was an exact duplicate of `source` — dashboards could already filter on `source=datamates`. `surface` stays on both events: `client_connected` has no `command`, so `surface` is the one key that joins the two funnel stages, and IDE-funnel reads filter `surface=="serve"`. - Add `test/server/altimate-client-connected-wiring.test.ts`: drives real requests through `Server.createApp` and asserts the middleware calls `Telemetry.clientConnected()` for `GET /event` and `GET /global/event`, and not for other paths or non-GET methods. Nothing previously exercised the server hook that produces the signal — deleting it or changing the path literals left the suite green. Verified non-vacuous by mutation. Tests: first-run-health, config-opt-out and release-v0.11.1-adversarial updated for the dropped field. Package typecheck clean; telemetry suites 394 pass. Co-Authored-By: Claude Opus 4.8 <[email protected]>
1 parent 6b08c14 commit a91fbe4

5 files changed

Lines changed: 79 additions & 20 deletions

File tree

‎packages/opencode/src/altimate/telemetry/index.ts‎

Lines changed: 3 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -674,10 +674,9 @@ export namespace Telemetry {
674674
session_id: string
675675
/** Top-level CLI command, e.g. "tui", "serve", "run". */
676676
command: string
677-
/** Same value as `command`, under the name dashboards query (Properties.surface). */
677+
/** Same value as `command`, under the name dashboards query (Properties.surface). Shared
678+
* with `client_connected`, which has no `command`, so the funnel joins on one key. */
678679
surface: string
679-
/** CLI client from ALTIMATE_CLI_CLIENT, e.g. "datamates" for the VS Code extension. */
680-
client: string
681680
/** Wall time from process start until the command could serve its first request or frame. */
682681
duration_ms: number
683682
/** True when this process emitted a non-upgrade first_launch, i.e. a brand-new machine. */
@@ -689,8 +688,8 @@ export namespace Telemetry {
689688
type: "client_connected"
690689
timestamp: number
691690
session_id: string
691+
/** Launching command/surface ("serve", "run", "tui"); IDE-funnel reads filter surface=="serve". */
692692
surface: string
693-
client: string
694693
/** Process uptime when the first client attached. */
695694
since_start_ms: number
696695
}
@@ -2191,7 +2190,6 @@ export namespace Telemetry {
21912190
session_id: sessionId,
21922191
command,
21932192
surface: command,
2194-
client: Flag.ALTIMATE_CLI_CLIENT,
21952193
duration_ms: Math.round(performance.now()),
21962194
fresh_install: freshInstall,
21972195
})
@@ -2206,7 +2204,6 @@ export namespace Telemetry {
22062204
timestamp: Date.now(),
22072205
session_id: sessionId,
22082206
surface: command,
2209-
client: Flag.ALTIMATE_CLI_CLIENT,
22102207
since_start_ms: Math.round(performance.now()),
22112208
})
22122209
}

‎packages/opencode/test/altimate/telemetry/first-run-health.test.ts‎

Lines changed: 2 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -47,35 +47,28 @@ describe("first-run health telemetry", () => {
4747
}
4848
})
4949

50-
test("startup_ready carries surface and client as top-level properties", () => {
50+
test("startup_ready carries surface as a top-level property", () => {
5151
const events: Telemetry.Event[] = []
5252
const spy = spyOn(Telemetry, "track").mockImplementation((event) => {
5353
events.push(event)
5454
})
55-
const saved = process.env.ALTIMATE_CLI_CLIENT
56-
process.env.ALTIMATE_CLI_CLIENT = "datamates"
5755
try {
5856
Telemetry.startupReady("serve")
5957
const event = events.find((e) => e.type === "startup_ready") as Extract<
6058
Telemetry.Event,
6159
{ type: "startup_ready" }
6260
>
6361
expect(event.surface).toBe("serve")
64-
expect(event.client).toBe("datamates")
6562
} finally {
66-
if (saved === undefined) delete process.env.ALTIMATE_CLI_CLIENT
67-
else process.env.ALTIMATE_CLI_CLIENT = saved
6863
spy.mockRestore()
6964
}
7065
})
7166

72-
test("client_connected is emitted exactly once per process with surface and client", () => {
67+
test("client_connected is emitted exactly once per process with surface", () => {
7368
const events: Telemetry.Event[] = []
7469
const spy = spyOn(Telemetry, "track").mockImplementation((event) => {
7570
events.push(event)
7671
})
77-
const saved = process.env.ALTIMATE_CLI_CLIENT
78-
process.env.ALTIMATE_CLI_CLIENT = "datamates"
7972
try {
8073
Telemetry.setCommand("serve")
8174
Telemetry.clientConnected()
@@ -87,11 +80,8 @@ describe("first-run health telemetry", () => {
8780
>[]
8881
expect(connected).toHaveLength(1)
8982
expect(connected[0].surface).toBe("serve")
90-
expect(connected[0].client).toBe("datamates")
9183
expect(connected[0].since_start_ms).toBeGreaterThan(0)
9284
} finally {
93-
if (saved === undefined) delete process.env.ALTIMATE_CLI_CLIENT
94-
else process.env.ALTIMATE_CLI_CLIENT = saved
9585
spy.mockRestore()
9686
}
9787
})
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
// altimate_change — regression test for the server-side wiring that produces `client_connected`.
2+
//
3+
// The telemetry unit tests (test/altimate/telemetry/first-run-health.test.ts) prove the
4+
// `Telemetry.clientConnected()` latch, but nothing exercised the `.use` middleware in
5+
// `Server.createApp` that actually calls it. Without this test, deleting the middleware, changing
6+
// the `/event` / `/global/event` path literals, dropping the `GET` guard, or moving the hook ahead
7+
// of basicAuth would all leave the suite green while silently killing the IDE-funnel signal.
8+
//
9+
// The middleware fires `void import("../altimate/telemetry").then((m) => m.Telemetry.clientConnected())`
10+
// after next() is set up, so the call lands on a later microtask; each case waits a tick before
11+
// asserting. We spy on `Telemetry.clientConnected` itself (not `track`) so the assertion is purely
12+
// about the middleware deciding to call it — the once-per-process latch lives inside that function
13+
// and is covered elsewhere.
14+
//
15+
// These cases catch deleting the middleware, changing the `/event` / `/global/event` path literals,
16+
// and dropping the `GET` guard. The remaining regression the reviewer named — moving the hook above
17+
// basicAuth so unauthenticated probes count — is not exercised here: auth is enforced by a separate
18+
// `basicAuth` middleware gated on `Flag.OPENCODE_SERVER_PASSWORD`, which is a module-level const
19+
// frozen at load (flag.ts), and CI runs the package in one Bun process, so a password set at test
20+
// time can't drive basicAuth to 401. The ordering is enforced structurally: this hook is registered
21+
// immediately after the basicAuth `.use` in `Server.createApp`.
22+
import { afterEach, beforeEach, describe, expect, spyOn, test } from "bun:test"
23+
import { Server } from "../../src/server/server"
24+
import { Telemetry } from "../../src/altimate/telemetry"
25+
import { resetDatabase } from "./db"
26+
import { disposeAllInstances } from "../fixture/fixture"
27+
28+
const tick = () => new Promise((r) => setTimeout(r, 15))
29+
30+
async function cancelBody(response: Response) {
31+
await response.body?.cancel().catch(() => {})
32+
}
33+
34+
let spy: ReturnType<typeof spyOn<typeof Telemetry, "clientConnected">>
35+
36+
beforeEach(() => {
37+
spy = spyOn(Telemetry, "clientConnected").mockImplementation(() => {})
38+
})
39+
40+
afterEach(async () => {
41+
spy.mockRestore()
42+
await disposeAllInstances()
43+
await resetDatabase()
44+
})
45+
46+
describe("client_connected server wiring", () => {
47+
test("GET /event invokes Telemetry.clientConnected", async () => {
48+
const response = await Server.Default().request("/event")
49+
await cancelBody(response)
50+
await tick()
51+
expect(spy).toHaveBeenCalled()
52+
})
53+
54+
test("GET /global/event invokes Telemetry.clientConnected", async () => {
55+
const response = await Server.Default().request("/global/event")
56+
await cancelBody(response)
57+
await tick()
58+
expect(spy).toHaveBeenCalled()
59+
})
60+
61+
test("a non-event path does not invoke Telemetry.clientConnected", async () => {
62+
const response = await Server.Default().request("/config")
63+
await cancelBody(response)
64+
await tick()
65+
expect(spy).not.toHaveBeenCalled()
66+
})
67+
68+
test("a non-GET request to /event does not invoke Telemetry.clientConnected", async () => {
69+
const response = await Server.Default().request("/event", { method: "POST" })
70+
await cancelBody(response)
71+
await tick()
72+
expect(spy).not.toHaveBeenCalled()
73+
})
74+
})

‎packages/opencode/test/skill/release-v0.11.1-adversarial.test.ts‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -466,7 +466,6 @@ describe("track — anchor events before init", () => {
466466
session_id: "pre-init",
467467
command: "run",
468468
surface: "run",
469-
client: "",
470469
duration_ms: 1,
471470
fresh_install: false,
472471
})

‎packages/opencode/test/telemetry/config-opt-out.test.ts‎

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -110,7 +110,6 @@ describe("Telemetry.init() inside an Instance context — config.telemetry.disab
110110
session_id: "config-opt-out-enabled",
111111
command: "test",
112112
surface: "test",
113-
client: "",
114113
duration_ms: 1,
115114
fresh_install: false,
116115
})

0 commit comments

Comments
 (0)