Skip to content

Commit 8e434f4

Browse files
leonhardprinzconsultingsultanedwinyjlim
authored
feat: add audit-3000 wizard command (#452)
Co-authored-by: Jon <148268521+consultingsultan@users.noreply.github.com> Co-authored-by: Edwin Lim <edwin@posthog.com>
1 parent 77fae72 commit 8e434f4

28 files changed

Lines changed: 1859 additions & 74 deletions

src/lib/agent/agent-interface.ts

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -288,6 +288,8 @@ export type AgentConfig = {
288288
/** Feature flag key -> variant (evaluated at start of run). */
289289
wizardFlags?: Record<string, string>;
290290
wizardMetadata?: Record<string, string>;
291+
/** Workflow identifier — selects the model for that workflow. */
292+
integrationLabel?: string;
291293
};
292294

293295
/**
@@ -663,10 +665,17 @@ export async function initializeAgent(
663665
});
664666
mcpServers['wizard-tools'] = wizardToolsServer;
665667

668+
// audit-3000 needs Opus 4.7's depth for the multi-phase audit chain;
669+
// every other workflow runs on Sonnet 4.6.
670+
const model =
671+
config.integrationLabel === 'audit-3000'
672+
? 'anthropic/claude-opus-4-7'
673+
: 'anthropic/claude-sonnet-4-6';
674+
666675
const agentRunConfig: AgentRunConfig = {
667676
workingDirectory: config.workingDirectory,
668677
mcpServers,
669-
model: 'anthropic/claude-sonnet-4-6',
678+
model,
670679
wizardFlags: config.wizardFlags,
671680
wizardMetadata: config.wizardMetadata,
672681
};

src/lib/agent/agent-runner.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,7 @@ export async function runWorkflow(
312312
skillsBaseUrl,
313313
wizardFlags,
314314
wizardMetadata,
315+
integrationLabel: config.integrationLabel,
315316
},
316317
sessionToOptions(session),
317318
);

src/lib/workflows/agent-skill/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ export function createSkillWorkflow(
5757
description: opts.description,
5858
flowKey: opts.flowKey,
5959
steps: AGENT_SKILL_STEPS,
60+
reportFile: opts.reportFile,
6061
run: {
6162
skillId: opts.skillId,
6263
integrationLabel: opts.integrationLabel,
Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
import fs from 'fs';
2+
import path from 'path';
3+
import {
4+
AGENT_SKILL_STEPS,
5+
createSkillWorkflow,
6+
} from '../agent-skill/index.js';
7+
import type { Workflow, WorkflowConfig } from '../workflow-step.js';
8+
import type { WorkflowRun } from '../../agent/agent-runner.js';
9+
import type { WizardSession } from '../../wizard-session.js';
10+
import { AUDIT_ABORT_CASES } from '../audit/detect.js';
11+
import {
12+
AUDIT_CHECKS_FILE,
13+
AUDIT_CHECKS_KEY,
14+
type AuditCheck,
15+
} from '../audit/types.js';
16+
import { AUDIT_SEED_CHECKS } from '../audit/seed.js';
17+
import { logToFile } from '../../../utils/debug';
18+
19+
const AUDIT3000_REPORT_FILE = 'posthog-audit-3000-report.md';
20+
21+
// Extra checks the v3000 audit adds on top of the base 10. IDs must match
22+
// those referenced in the audit-3000 skill's step files (Event Quality,
23+
// stale feature-flag review, session replay [fix + optimize], per-product
24+
// use-case expansion, and phase markers for the post-flags chain).
25+
const AUDIT3000_EXTRA_CHECKS: AuditCheck[] = [
26+
// ── Event Quality (Step 5) ──
27+
{
28+
id: 'event-naming-standardization',
29+
area: 'Event Quality',
30+
label: 'Event naming convention is consistent',
31+
status: 'pending',
32+
},
33+
{
34+
id: 'event-duplicates-and-bloat',
35+
area: 'Event Quality',
36+
label: 'No duplicate or bloated event capture',
37+
status: 'pending',
38+
},
39+
{
40+
id: 'event-quality-context-review',
41+
area: 'Event Quality',
42+
label: 'Event property context reviewed',
43+
status: 'pending',
44+
},
45+
{
46+
id: 'event-usage-coverage',
47+
area: 'Event Quality',
48+
label: 'Captured events match insights / dashboards usage',
49+
status: 'pending',
50+
},
51+
// ── Feature Flags (Step 6) ──
52+
{
53+
id: 'stale-feature-flags-reviewed',
54+
area: 'Feature Flags',
55+
label: 'Stale feature flags reviewed',
56+
status: 'pending',
57+
},
58+
// ── Session Replay — fix (Step 6b) ──
59+
{
60+
id: 'replay-minimum-duration-set',
61+
area: 'Session Replay',
62+
label: 'Minimum duration set on init',
63+
status: 'pending',
64+
},
65+
{
66+
id: 'replay-mask-config',
67+
area: 'Session Replay',
68+
label: 'Mask config covers sensitive surfaces',
69+
status: 'pending',
70+
},
71+
{
72+
id: 'replay-disabled-in-test-envs',
73+
area: 'Session Replay',
74+
label: 'Disabled in test / CI environments',
75+
status: 'pending',
76+
},
77+
{
78+
id: 'replay-strict-minimum-duration',
79+
area: 'Session Replay',
80+
label: 'Strict minimum duration enforced',
81+
status: 'pending',
82+
},
83+
// ── Session Replay — optimize (Step 6b cost wave) ──
84+
{
85+
id: 'replay-sampling-rate',
86+
area: 'Session Replay — Optimize',
87+
label: 'Sampling rate tuned for cost',
88+
status: 'pending',
89+
},
90+
{
91+
id: 'replay-triggers-configured',
92+
area: 'Session Replay — Optimize',
93+
label: 'Triggers configured (event / URL / flag)',
94+
status: 'pending',
95+
},
96+
{
97+
id: 'replay-network-recording-filtered',
98+
area: 'Session Replay — Optimize',
99+
label: 'Network recording filtered',
100+
status: 'pending',
101+
},
102+
{
103+
id: 'replay-mobile-sampling',
104+
area: 'Session Replay — Optimize',
105+
label: 'Mobile sampling configured',
106+
status: 'pending',
107+
},
108+
// ── Use Case: Expansion (Step 9) ──
109+
{
110+
id: 'expansion-product-analytics',
111+
area: 'Use Case: Expansion',
112+
label: 'Product analytics coverage',
113+
status: 'pending',
114+
},
115+
{
116+
id: 'expansion-error-tracking',
117+
area: 'Use Case: Expansion',
118+
label: 'Error tracking coverage',
119+
status: 'pending',
120+
},
121+
{
122+
id: 'expansion-llm-observability',
123+
area: 'Use Case: Expansion',
124+
label: 'LLM observability coverage',
125+
status: 'pending',
126+
},
127+
{
128+
id: 'expansion-session-replay',
129+
area: 'Use Case: Expansion',
130+
label: 'Session replay coverage',
131+
status: 'pending',
132+
},
133+
{
134+
id: 'expansion-feature-flags',
135+
area: 'Use Case: Expansion',
136+
label: 'Feature flags coverage',
137+
status: 'pending',
138+
},
139+
{
140+
id: 'expansion-surveys',
141+
area: 'Use Case: Expansion',
142+
label: 'Surveys coverage',
143+
status: 'pending',
144+
},
145+
{
146+
id: 'expansion-logs',
147+
area: 'Use Case: Expansion',
148+
label: 'Logs coverage',
149+
status: 'pending',
150+
},
151+
{
152+
id: 'expansion-web-analytics',
153+
area: 'Use Case: Expansion',
154+
label: 'Web analytics coverage',
155+
status: 'pending',
156+
},
157+
// ── Additional Sections (Steps 7, 8, 10 phase markers) ──
158+
// Tracked in the ledger so the UI can surface "did it run / was it
159+
// skipped" alongside the regular checks. use-case-expansion is omitted
160+
// because the eight `expansion-*` checks above cover that phase.
161+
{
162+
id: 'customer-enrichment',
163+
area: 'Additional Sections',
164+
label: 'Customer enrichment (Harmonic + PDL)',
165+
status: 'pending',
166+
},
167+
{
168+
id: 'use-case-match',
169+
area: 'Additional Sections',
170+
label: 'Use-case match',
171+
status: 'pending',
172+
},
173+
{
174+
id: 'final-report',
175+
area: 'Additional Sections',
176+
label: 'Final audit report written',
177+
status: 'pending',
178+
},
179+
];
180+
181+
const AUDIT3000_SEED_CHECKS: AuditCheck[] = [
182+
...AUDIT_SEED_CHECKS,
183+
...AUDIT3000_EXTRA_CHECKS,
184+
];
185+
186+
// Audit-3000 has its own arcade-flavoured intro / run / outro screens. The
187+
// shared audit screens stay reserved for the original `audit` workflow.
188+
const AUDIT3000_SCREEN_BY_STEP: Record<string, string> = {
189+
intro: 'audit-3000-intro',
190+
run: 'audit-3000-run',
191+
outro: 'audit-3000-outro',
192+
};
193+
194+
const seedAudit3000Ledger = (installDir: string): void => {
195+
const target = path.join(installDir, AUDIT_CHECKS_FILE);
196+
const tmp = `${target}.tmp`;
197+
fs.writeFileSync(tmp, JSON.stringify(AUDIT3000_SEED_CHECKS, null, 2), 'utf8');
198+
fs.renameSync(tmp, target);
199+
logToFile(
200+
`seedAudit3000Ledger: wrote ${AUDIT3000_SEED_CHECKS.length} entries to ${target}`,
201+
);
202+
};
203+
204+
const seedBeforeAudit3000Run = (session: WizardSession): void => {
205+
seedAudit3000Ledger(session.installDir);
206+
session.frameworkContext[AUDIT_CHECKS_KEY] = AUDIT3000_SEED_CHECKS;
207+
};
208+
209+
const withAudit3000Screens = (steps: Workflow): Workflow =>
210+
steps.map((step) => {
211+
const override = AUDIT3000_SCREEN_BY_STEP[step.id];
212+
return override ? { ...step, screen: override } : step;
213+
});
214+
215+
const audit3000Steps: Workflow = withAudit3000Screens(AGENT_SKILL_STEPS);
216+
217+
const baseConfig = createSkillWorkflow({
218+
skillId: 'audit-3000',
219+
command: 'audit-3000',
220+
flowKey: 'audit-3000',
221+
description:
222+
'Audit an existing PostHog integration (v3000 — adds event quality, stale-flag hygiene, customer enrichment, use-case match)',
223+
integrationLabel: 'audit-3000',
224+
customPrompt:
225+
'Run the audit-3000 skill end-to-end. Follow the step chain starting at references/1-version.md. Do not modify any project files — only create the final audit report and (when enrichment is enabled) the enrichment report.',
226+
successMessage: `Audit complete! View the report at ./${AUDIT3000_REPORT_FILE}`,
227+
reportFile: AUDIT3000_REPORT_FILE,
228+
docsUrl: 'https://posthog.com/docs/product-analytics/best-practices',
229+
spinnerMessage: 'Running PostHog Audit 3000...',
230+
estimatedDurationMinutes: 6,
231+
requires: ['posthog-integration'],
232+
abortCases: AUDIT_ABORT_CASES,
233+
});
234+
235+
const audit3000Run = async (session: WizardSession): Promise<WorkflowRun> => {
236+
seedBeforeAudit3000Run(session);
237+
238+
if (!baseConfig.run) {
239+
throw new Error('audit-3000 workflow has no run configuration.');
240+
}
241+
242+
return typeof baseConfig.run === 'function'
243+
? baseConfig.run(session)
244+
: baseConfig.run;
245+
};
246+
247+
export const audit3000Config: WorkflowConfig = {
248+
...baseConfig,
249+
steps: audit3000Steps,
250+
run: audit3000Run,
251+
};

src/lib/workflows/workflow-registry.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,12 +15,14 @@ import type { WorkflowConfig } from './workflow-step.js';
1515
import { posthogIntegrationConfig } from './posthog-integration/index.js';
1616
import { revenueAnalyticsConfig } from './revenue-analytics/index.js';
1717
import { auditConfig } from './audit/index.js';
18+
import { audit3000Config } from './audit-3000/index.js';
1819
import { posthogDoctorConfig } from './posthog-doctor/index.js';
1920

2021
export const WORKFLOW_REGISTRY: WorkflowConfig[] = [
2122
posthogIntegrationConfig,
2223
revenueAnalyticsConfig,
2324
auditConfig,
25+
audit3000Config,
2426
posthogDoctorConfig,
2527
];
2628

src/lib/workflows/workflow-step.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,12 @@ export interface WorkflowConfig {
123123
run?: WorkflowRun | ((session: WizardSession) => Promise<WorkflowRun>);
124124
/** Prerequisites: other workflow flowKeys that must have run first */
125125
requires?: string[];
126+
/**
127+
* Path (relative to installDir) of the report file the workflow writes.
128+
* Mirrors `run.reportFile` but lifted to the top level so UI screens can
129+
* read it synchronously without resolving a deferred `run` function.
130+
*/
131+
reportFile?: string;
126132
}
127133

128134
/**

src/ui/tui/flows.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ export enum Screen {
2727
AuditIntro = 'audit-intro',
2828
AuditRun = 'audit-run',
2929
AuditOutro = 'audit-outro',
30+
Audit3000Intro = 'audit-3000-intro',
31+
Audit3000Run = 'audit-3000-run',
32+
Audit3000Outro = 'audit-3000-outro',
3033
HealthCheck = 'health-check',
3134
DoctorIntro = 'doctor-intro',
3235
DoctorReport = 'doctor-report',
@@ -46,6 +49,7 @@ export enum Flow {
4649
PostHogIntegration = 'posthog-integration',
4750
RevenueAnalyticsSetup = 'revenue-analytics-setup',
4851
Audit = 'audit',
52+
Audit3000 = 'audit-3000',
4953
PosthogDoctor = 'posthog-doctor',
5054
AgentSkill = 'agent-skill',
5155
McpAdd = 'mcp-add',

src/ui/tui/screen-registry.tsx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ import { AgentSkillIntroScreen } from './screens/AgentSkillIntroScreen.js';
2424
import { AuditIntroScreen } from './screens/audit/AuditIntroScreen.js';
2525
import { AuditRunScreen } from './screens/audit/AuditRunScreen.js';
2626
import { AuditOutroScreen } from './screens/audit/AuditOutroScreen.js';
27+
import { Audit3000IntroScreen } from './screens/audit-3000/Audit3000IntroScreen.js';
28+
import { Audit3000RunScreen } from './screens/audit-3000/Audit3000RunScreen.js';
29+
import { Audit3000OutroScreen } from './screens/audit-3000/Audit3000OutroScreen.js';
2730
import { SetupScreen } from './screens/SetupScreen.js';
2831
import { AuthScreen } from './screens/AuthScreen.js';
2932
import { RunScreen } from './screens/RunScreen.js';
@@ -63,6 +66,9 @@ export function createScreens(
6366
[Screen.AuditIntro]: <AuditIntroScreen store={store} />,
6467
[Screen.AuditRun]: <AuditRunScreen store={store} />,
6568
[Screen.AuditOutro]: <AuditOutroScreen store={store} />,
69+
[Screen.Audit3000Intro]: <Audit3000IntroScreen store={store} />,
70+
[Screen.Audit3000Run]: <Audit3000RunScreen store={store} />,
71+
[Screen.Audit3000Outro]: <Audit3000OutroScreen store={store} />,
6672
[Screen.HealthCheck]: <HealthCheckScreen store={store} />,
6773
[Screen.DoctorIntro]: <DoctorIntroScreen store={store} />,
6874
[Screen.DoctorReport]: <DoctorReportScreen store={store} />,

0 commit comments

Comments
 (0)