Skip to content

Commit 78d0653

Browse files
Silthusclaude
andcommitted
chore(workflows): pin which first run banner asks an agent for help
Red step: the logic stub answers no missing data yet. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
1 parent 6fefaff commit 78d0653

2 files changed

Lines changed: 151 additions & 0 deletions

File tree

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,126 @@
1+
import { expectLogic } from 'kea-test-utils'
2+
import posthog from 'posthog-js'
3+
4+
import { useMocks } from '~/mocks/jest'
5+
import { initKeaTests } from '~/test/init'
6+
7+
import type { HogFlowTemplateApi } from '../generated/api.schemas'
8+
import { firstRunAgentHelpLogic } from './firstRunAgentHelpLogic'
9+
10+
const WELCOME = '019b6f44-f9a3-0000-c4a7-b8050d25d690'
11+
12+
const TEMPLATES: Partial<HogFlowTemplateApi>[] = [
13+
{
14+
id: 'onboarding',
15+
name: 'Onboarding',
16+
scope: 'global',
17+
starts_on: { kind: 'event', events: ['$pageview'], detail: '' },
18+
tags: [],
19+
actions: [],
20+
},
21+
{
22+
id: WELCOME,
23+
name: 'Welcome',
24+
scope: 'global',
25+
starts_on: { kind: 'event', events: ['user signed up', 'signed_up'], detail: '' },
26+
tags: [],
27+
actions: [],
28+
},
29+
]
30+
31+
describe('firstRunAgentHelpLogic', () => {
32+
let logic: ReturnType<typeof firstRunAgentHelpLogic.build>
33+
let capture: jest.SpyInstance
34+
35+
let seenEvents: string[]
36+
let personProperties: string[]
37+
38+
beforeEach(() => {
39+
seenEvents = []
40+
personProperties = []
41+
useMocks({
42+
get: {
43+
'/api/projects/:team_id/hog_flow_templates/': { count: TEMPLATES.length, results: TEMPLATES },
44+
'/api/projects/:team_id/event_definitions/': ({ request }) => {
45+
const names = new URL(request.url).searchParams.get('names')?.split(',') ?? []
46+
const seen = names.filter((name) => seenEvents.includes(name))
47+
return [
48+
200,
49+
{
50+
count: seen.length,
51+
results: seen.map((name) => ({ id: name, name, last_seen_at: '2026-10-01T00:00:00Z' })),
52+
},
53+
]
54+
},
55+
'/api/projects/:team_id/property_definitions/': ({ request }) => {
56+
const params = new URL(request.url).searchParams
57+
const names = params.get('properties')?.split(',') ?? []
58+
const definitions =
59+
params.get('type') === 'person' ? names.filter((name) => personProperties.includes(name)) : []
60+
return [
61+
200,
62+
{ count: definitions.length, results: definitions.map((name) => ({ id: name, name })) },
63+
]
64+
},
65+
},
66+
})
67+
initKeaTests()
68+
capture = jest.spyOn(posthog, 'capture').mockImplementation()
69+
})
70+
71+
async function openFirstRun(project: { seenEvents: string[]; personProperties: string[] }): Promise<void> {
72+
seenEvents = project.seenEvents
73+
personProperties = project.personProperties
74+
logic = firstRunAgentHelpLogic()
75+
logic.mount()
76+
await expectLogic(logic).toFinishAllListeners()
77+
}
78+
79+
afterEach(() => {
80+
logic?.unmount()
81+
jest.restoreAllMocks()
82+
})
83+
84+
const agentHelpShownCalls = (): unknown[][] =>
85+
capture.mock.calls.filter(([event]) => event === 'workflows first run agent help shown')
86+
87+
it.each([
88+
{
89+
project: 'without a signup event',
90+
seenEvents: ['$pageview'],
91+
personProperties: ['email'],
92+
missing: 'signup_event',
93+
},
94+
{
95+
project: 'with signups but no email property',
96+
seenEvents: ['signed_up'],
97+
personProperties: ['name'],
98+
missing: 'email',
99+
},
100+
{
101+
project: 'without a signup event or an email property',
102+
seenEvents: [],
103+
personProperties: [],
104+
missing: 'signup_event',
105+
},
106+
{
107+
project: 'with signups and an email property',
108+
seenEvents: ['signed_up', '$pageview'],
109+
personProperties: ['email'],
110+
missing: null,
111+
},
112+
])('asks a project $project for help with $missing', async ({ missing, ...project }) => {
113+
await openFirstRun(project)
114+
115+
expect(logic.values.missingData).toEqual(missing)
116+
expect(agentHelpShownCalls()).toEqual(missing ? [['workflows first run agent help shown', { missing }]] : [])
117+
})
118+
119+
it('reports a copied prompt with the data it fixes', async () => {
120+
await openFirstRun({ seenEvents: ['signed_up'], personProperties: [] })
121+
122+
logic.actions.reportPromptCopied()
123+
124+
expect(capture).toHaveBeenCalledWith('workflows first run agent prompt copied', { missing: 'email' })
125+
})
126+
})
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { MakeLogicType, actions, kea, path, selectors } from 'kea'
2+
3+
export type MissingData = 'signup_event' | 'email'
4+
5+
// Generated by kea-typegen. Update if you're an agent, ignore if you're human.
6+
export interface firstRunAgentHelpLogicValues {
7+
missingData: MissingData | null
8+
}
9+
10+
// Generated by kea-typegen. Update if you're an agent, ignore if you're human.
11+
export interface firstRunAgentHelpLogicActions {
12+
reportPromptCopied: () => true
13+
}
14+
15+
export type firstRunAgentHelpLogicType = MakeLogicType<firstRunAgentHelpLogicValues, firstRunAgentHelpLogicActions>
16+
17+
export const firstRunAgentHelpLogic = kea<firstRunAgentHelpLogicType>([
18+
path(['products', 'workflows', 'frontend', 'firstRun', 'firstRunAgentHelpLogic']),
19+
actions({
20+
reportPromptCopied: true,
21+
}),
22+
selectors({
23+
missingData: [() => [], (): MissingData | null => null],
24+
}),
25+
])

0 commit comments

Comments
 (0)