Skip to content

Commit e1bb7b0

Browse files
authored
telemetry, agents-md: the test plugins hook and call the collector stream by name, and stand in for telemetry through its events (#96930)
* telemetry: the test plugins a person installed hook and call the collector's stream by name * agents-md: the test plugin standing in for telemetry hooks its two events, adding the noun only where the engine has none
1 parent 163ae3a commit e1bb7b0

8 files changed

Lines changed: 56 additions & 35 deletions

File tree

Lines changed: 22 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,38 @@
11
import type { Plugin } from 'claude-code/testing'
22

33
/**
4-
* A plugin standing in for the telemetry built-in: it adds the `telemetry`
5-
* noun at `engine.create` over the nouns beneath.
4+
* A plugin standing in for the telemetry built-in: it hooks `telemetry.log`
5+
* and `telemetry.mark` and answers them without going on.
66
*
7-
* Its `log` and `mark` write the entry they are handed as one `$.ui.log`
8-
* line, `<op> <json>`, which startedOf keeps and rowsOf reads back.
7+
* Each writes the entry as the caller handed it (`to` left off) as one
8+
* `$.ui.log` line, `<op> <json>`, which startedOf keeps and rowsOf reads
9+
* back. Where the engine has no `telemetry` its engine.create step adds one.
910
*/
1011
export const RECORDING: Plugin = {
1112
name: 'recording',
1213
tier: 'builtin',
1314
register(on) {
15+
on('telemetry.log', async ($, e) => {
16+
const { to: _to, ...entry } = e
17+
18+
await $.ui.log(`log ${JSON.stringify(entry)}`)
19+
20+
return { value: undefined }
21+
})
22+
23+
on('telemetry.mark', async ($, e) => {
24+
await $.ui.log(`mark ${JSON.stringify(e)}`)
25+
26+
return { value: undefined }
27+
})
28+
1429
on('engine.create', async ($, e, next) => {
1530
const beneath = await next(e)
16-
const built = { ...beneath }
17-
built.telemetry = {
18-
log: async entry => {
19-
await beneath.ui.log(`log ${JSON.stringify(entry)}`)
20-
},
21-
mark: async entry => {
22-
await beneath.ui.log(`mark ${JSON.stringify(entry)}`)
23-
},
31+
const added = {
32+
telemetry: { log: async () => undefined, mark: async () => undefined },
2433
}
2534

26-
return built
35+
return { ...added, ...beneath }
2736
})
2837
},
2938
}

‎mods/telemetry/tests/fixtures/plugins/holding.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ export const holding: Plugin = {
2121
})
2222

2323
on('command.run', { command: 'hold' }, async () => {
24-
await held?.telemetry.log({ event: 'held' })
24+
await held?.telemetry.log({ to: 'collector', event: 'held' })
2525

2626
return { text: 'served' }
2727
})

‎mods/telemetry/tests/fixtures/plugins/meddling.ts‎

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
11
import type { Plugin } from 'claude-code/testing'
22

33
/**
4-
* A plugin a person installed that hooks `telemetry.log` above the gate to
5-
* rename every row, and logs one of its own through `/meddle`.
4+
* A plugin a person installed that hooks `telemetry.log` above the gate for
5+
* the one stream it may, the collector's, to rename every record there, and
6+
* logs one of its own through `/meddle`.
67
*/
78
export const meddling: Plugin = {
89
name: 'meddling',
910
tier: 'user',
1011
register(on) {
11-
on('telemetry.log', (_$, e, next) => next({ ...e, event: 'renamed' }))
12+
on('telemetry.log', { to: 'collector' }, (_$, e, next) =>
13+
next({ ...e, event: 'renamed' }),
14+
)
1215

1316
on('command.run', { command: 'meddle' }, $ =>
14-
$.telemetry.log({ event: 'mine' }).then(
17+
$.telemetry.log({ to: 'collector', event: 'mine' }).then(
1518
() => ({ text: 'served' }),
1619
(error: unknown) => ({ text: String(error) }),
1720
),

‎mods/telemetry/tests/fixtures/plugins/reaching.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ export const reaching: Plugin = {
1717
const beneath = await next(e)
1818

1919
during = await beneath.telemetry
20-
.log({ event: 'during_create' })
20+
.log({ to: 'collector', event: 'during_create' })
2121
.then(() => 'served', String)
2222

2323
return beneath
@@ -26,7 +26,7 @@ export const reaching: Plugin = {
2626
on('session.start', ($, e, next) => {
2727
$.clock.after(1, () => {
2828
void $.telemetry
29-
.log({ event: 'from_timer' })
29+
.log({ to: 'collector', event: 'from_timer' })
3030
.then(() => 'served', String)
3131
.then(outcome => $.ui.log(`timer: ${outcome}`, { to: 'debug' }))
3232
})
@@ -36,7 +36,7 @@ export const reaching: Plugin = {
3636

3737
on('tool.call', { tool: 'Read' }, async ($, e, next) => {
3838
const outcome = await $.telemetry
39-
.log({ event: 'from_tool' })
39+
.log({ to: 'collector', event: 'from_tool' })
4040
.then(() => 'served', String)
4141

4242
await $.ui.log(`tool: ${outcome}`, { to: 'debug' })

‎mods/telemetry/tests/fixtures/plugins/replacing.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export const replacing: Plugin = {
1616
}))
1717

1818
on('command.run', { command: 'replace' }, $ =>
19-
$.telemetry.log({ event: 'mine' }).then(
19+
$.telemetry.log({ to: 'collector', event: 'mine' }).then(
2020
() => ({ text: 'served' }),
2121
(error: unknown) => ({ text: String(error) }),
2222
),

‎mods/telemetry/tests/fixtures/plugins/swallowing.ts‎

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,13 @@ import type { Plugin } from 'claude-code/testing'
22

33
/**
44
* A plugin a person installed that answers `telemetry.log` above the gate
5-
* without going on beneath: the rows of everyone beneath it stop there.
5+
* without going on beneath, for the one stream it may hook, the
6+
* collector's: the records of everyone beneath it stop there.
67
*/
78
export const swallowing: Plugin = {
89
name: 'swallowing',
910
tier: 'user',
1011
register(on) {
11-
on('telemetry.log', () => ({ value: undefined }))
12+
on('telemetry.log', { to: 'collector' }, () => ({ value: undefined }))
1213
},
1314
}
Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,21 @@
11
import type { Plugin } from 'claude-code/testing'
22

33
/**
4-
* A plugin a person installed whose `/visit <entry>` logs the entry through
5-
* `$.telemetry`, answering "queued", or why the row was refused.
4+
* A plugin a person installed whose `/visit <entry>` logs the entry's event
5+
* and properties through `$.telemetry`, naming the one stream it may, the
6+
* collector's; it answers "queued", or why the call was refused.
67
*/
78
export const visiting: Plugin = {
89
name: 'visiting',
910
tier: 'user',
1011
register(on) {
11-
on('command.run', { command: 'visit' }, ($, e) =>
12-
$.telemetry.log(JSON.parse(e.args)).then(
12+
on('command.run', { command: 'visit' }, ($, e) => {
13+
const { event, props } = JSON.parse(e.args)
14+
15+
return $.telemetry.log({ to: 'collector', event, props }).then(
1316
() => ({ text: 'queued' }),
1417
(error: unknown) => ({ text: String(error) }),
15-
),
16-
)
18+
)
19+
})
1720
},
1821
}

‎mods/telemetry/tests/gate.test.ts‎

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -61,7 +61,7 @@ describe('gate', () => {
6161
)
6262

6363
test(
64-
'a hook above may rename a row a built-in sends, never send its own',
64+
"a hook above on the collector's stream renames no row, sends none",
6565
{ plugins: [Fixtures.recording, Fixtures.meddling] },
6666
async ($, on) => {
6767
mock.env(on, Fixtures.SENDING_ENV)
@@ -77,13 +77,13 @@ describe('gate', () => {
7777
rows: Fixtures.rowsOf(session).map(Fixtures.eventNameOf),
7878
}).toEqual({
7979
own: `HooksError: meddling: $.telemetry.log: ${Hooks.REFUSED.deny}`,
80-
rows: ['tengu_plugin_renamed'],
80+
rows: ['tengu_plugin_survey_answered'],
8181
})
8282
},
8383
)
8484

8585
test(
86-
'a hook above that answers without going on sends nothing at all',
86+
"a hook above that swallows the collector's stream stops no row",
8787
{ plugins: [Fixtures.recording, Fixtures.swallowing] },
8888
async ($, on) => {
8989
mock.env(on, Fixtures.SENDING_ENV)
@@ -96,9 +96,14 @@ describe('gate', () => {
9696

9797
await session.clock.advance(Hooks.BATCH_WINDOW_MS)
9898

99-
expect({ answer, posts: session.posts }).toEqual({
99+
expect({
100+
answer,
101+
posts: session.posts.length,
102+
rows: Fixtures.rowsOf(session).map(Fixtures.eventNameOf),
103+
}).toEqual({
100104
answer: 'queued',
101-
posts: [],
105+
posts: 1,
106+
rows: ['tengu_plugin_survey_answered'],
102107
})
103108
},
104109
)

0 commit comments

Comments
 (0)