Skip to content

feat(workflows): add a classify with ai workflow action - #114006

Merged
trunk-io[bot] merged 15 commits into
masterfrom
posthog/workflows-classify-with-jev
Oct 12, 2026
Merged

trunk-io[bot] merged 15 commits into
masterfrom
posthog/workflows-classify-with-jev

Conversation

@mayteio

@mayteio mayteio commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

Problem

Changes

  • With workflow-classify-action on, the builder offers a "Classify with AI" step. The author sets a question, a JSON context, 2 to 16 categories, and a model.
  • The model picker is per step and offers Jev only for now. The endpoint accepts only models on an allowlist.
  • GPT-6 Luna is wired but commented out. The AI gateway already routes it on /v1/systemone (ai-gateway#528).
  • The step stores category and confidence for a regular conditional branch.
  • The CDP worker posts to a new internal route, workflow_classifications, with a scoped service JWT that carries the workflow id.
  • The route asks the model through the ml_inference facade with privacy mode on. Event data goes only into the decision state, never the instructions.
  • The route returns 422 for an answer the step cannot branch on, and 400 for context nested over 100 levels.
  • A save applies the same limits, so a saved step cannot fail on its config at run time.
  • A step test run mocks the call, so it spends no AI credits.
  • The worker does not retry its own 7 second timeout, because the gateway may still be answering. callInternalApi gets a retryOnTimeout option, on by default.
  • The template flag gate now passes the project uuid, because a project id repeats across PostHog instances. workflow-classify-action targets project 2 by uuid.
  • Mechanical: template and route registration, JWT setting and audience, a Hog bytecode fixture, and a facade-only tach.toml entry.

Classify with AI step configuration with the model picker

Warning

Deferred while the step is dogfooded: a per-project rate limit, and a total deadline on the gateway call. A gateway read timeout still returns a 503, which the worker retries.
Before GPT-6 Luna is enabled: OpenAI's processing of workflow context needs sign-off, because the model sends that context to OpenAI.

How did you test this code?

Test rationale: the endpoint tests mock the facade. They assert privacy mode and the model sent, reject a model off the allowlist, and cover answers the facade accepts but the step cannot branch on. classify.test.ts checks that a timeout makes one attempt and a refused connection makes three.

Manual run on a local stack with a stub gateway: a billing ticket took the billing branch, a product question took the default branch. Both requests carried the Jev model id and privacy mode.

Run logs showing the category stored and the branch taken

Production jevk5 labeled the same synthetic tickets correctly (billing, support, spam) at about 0.98 confidence through HogQL decide().

Live run against dev Jev: a local ai-gateway, set up as in its "Try a decision host locally" guide, forwarded to the dev hogference-jev service. A test run with mocked calls off sent a synthetic double-charge ticket through the step. It returned billing at 0.997 confidence, then moved to the exit step.

👉 Stay up-to-date with PostHog coding conventions for a smoother review.

Release status

  • This change is behind a feature flag and is not available to users

Docs update

None. The step is hidden and flag-gated.

🤖 Agent context

Autonomy: Human-driven (agent-assisted)

Agent: Claude Code in PostHog Desktop, Claude Opus 5.5 (claude-opus-5-5)

  • Rebased Michael Reichenbach's feat(workflows): add a classify with jev workflow action #111710 onto master and moved its model call onto the ml_inference facade.
  • Renamed from "Classify with Jev" before dogfooding, so no saved workflow carries the old template id or variable names.
  • Carries the cross-lane-change label because the endpoint and the worker must land together.
  • Added GPT-6 Luna as a second model, then hid it until the vendor sign-off lands.
  • Skills invoked: /improving-drf-endpoints, /writing-tests, /writing-pr-descriptions.
  • All test data is synthetic.

🤖 Generated with Claude Code


Created with PostHog Desktop

@mayteio mayteio self-assigned this Oct 8, 2026
@trunk-io

trunk-io Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

😎 Merged successfully - details.

@mayteio mayteio added the reviewhog label Oct 8, 2026 — with PostHog
@posthog

posthog Bot commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

🦔 PostHog Review reviewed this pull request

Found 0 must fix, 2 should fix, 1 consider.

Published 3 findings (view the review).

Resolved comments: 1 fixed, 2 left for you

@github-actions

github-actions Bot commented Oct 8, 2026 •

Copy link
Copy Markdown
Contributor

🤖 CI report

⚠️ Trunk lane — backend Python lane, mixes lanes

This PR is assigned to the backend Python lane. It runs backend Python tests and may merge in parallel with PRs in other lanes.

This PR changes Python or frontend code (frontend/src/lib/constants.tsx, posthog/cdp/flag_gated_templates.py, posthog/jwt.py and 9 more) and Node or Rust code (nodejs/src/cdp/async-functions/classify.test.ts, nodejs/src/cdp/async-functions/classify.ts, nodejs/src/cdp/async-functions/index.ts and 8 more) together. In the merge queue, every Node or Rust PR behind it in the same lane then runs the Django and frontend suites too, so the cross-lane check fails. Split it, or add the cross-lane-change label if the halves must land together.

✅ Complexity (TypeScript) — clean

Cyclomatic complexity above the limit in changed typescript files (10 for production files, 15 for test files). Warn only: worth simplifying when you next touch these functions.

⚠️ Duplication (Python) — 1 new duplicated block (worst 82 tokens)

New Python code duplication introduced by this branch. Fails at 70+ tokens in app code, or 150+ tokens when both copies live in test files. Advisory while the gate proves itself: extract a shared helper instead of copying.

First copy Second copy Lines Tokens
posthog/cdp/flag_gated_templates.py:27 products/conversations/backend/temporal/coordinator.py:116 15 82
✅ Duplication (TypeScript) — clean

New TypeScript code duplication introduced by this branch. Fails at 70+ tokens in app code, or 150+ tokens when both copies live in test files. Advisory while the gate proves itself: extract a shared helper instead of copying.

⚠️ Comment density — 5% of added code lines are comments (46 of 845)

This section warns when comments are more than 3% of the code lines a PR adds, and alerts above 6%. Before agent-assisted PRs, the typical share was about 2%. Only full-line comments count. Docstrings, generated files, snapshots, migrations, and workflow files are left out.

Comments that restate the code, record how the change came about, or narrate the next line add noise for the next reader. Keep the comments that explain a reason the code cannot show, and remove the rest. See .agents/skills/writing-code-comments/SKILL.md for the house rules.

Files with the most added comment lines:

File Comment lines Added lines
products/workflows/backend/presentation/views/workflow_classifications.py 15 217
nodejs/src/cdp/async-functions/classify.ts 9 122
nodejs/src/cdp/async-functions/classify.test.ts 3 104
nodejs/src/cdp/templates/_destinations/posthog_workflows/posthog-classify.template.ts 3 67
products/workflows/backend/presentation/views/hog_flow.py 3 17
nodejs/src/cdp/async-functions/internal-api-call.ts 2 19
nodejs/src/cdp/config.ts 2 4
nodejs/src/cdp/utils/cdp-fetch.ts 2 8

This check does not block merging. It updates on every push and clears when the share drops.

⚠️ Bundle size — 🔺 +492 B (+0.0%)

Uncompressed size of every built .js bundle, compared against the base branch.

Total: 75.84 MiB · 🔺 +492 B (+0.0%)

No file changed by more than 1000 B.

Posted automatically by build-bundle-size-report · uncompressed bytes from dist-report

✅ Eager graph — within budget

How much code each root ships on the eager path — downloaded and parsed before the surface is interactive. Measured from the esbuild output chunks (post-tree-shake, static imports only); lazy import() / React.lazy chunks are not counted.

Root Eager (shipped) Δ vs base Budget
entry (logged-out pages, app bootstrap)
src/index.tsx
1002.1 KiB · 23 files no change █████████░ 85.5% of 1.14 MiB
logged-out boot: index + App + bootApp (preloaded by every page, including /login)
src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
2.86 MiB · 615 files 🔺 +52 B (+0.0%) █████████░ 94.4% of 3.03 MiB
authenticated shell (every logged-in page)
src/scenes/AuthenticatedShell.tsx
7.27 MiB · 2,509 files 🔺 +52 B (+0.0%) ██████████ 95.0% of 7.64 MiB
dashboard scene
src/scenes/dashboard/Dashboard.tsx
9.51 MiB · 3,589 files 🔺 +52 B (+0.0%) █████████░ 92.3% of 10.30 MiB
today home path
src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
7.29 MiB · 2,519 files 🔺 +52 B (+0.0%) █████████░ 92.4% of 7.89 MiB
events scene
src/scenes/activity/explore/EventsScene.tsx
9.09 MiB · 3,424 files 🔺 +52 B (+0.0%) █████████░ 92.1% of 9.88 MiB
replay detail scene
src/scenes/session-recordings/detail/SessionRecordingDetail.tsx
11.89 MiB · 4,267 files 🔺 +52 B (+0.0%) ████████░░ 79.0% of 15.05 MiB

🟢 node_modules/monaco-editor/ stays out of src/index.tsx
🟢 src/lib/components/ActivityLog/describers stays out of src/index.tsx
🟢 node_modules/@posthog/brand/dist/generated/hoggies/svg/ stays out of src/index.tsx
🟢 node_modules/@posthog/brand/dist/generated/hoggies/components/ stays out of src/index.tsx
🟢 node_modules/monaco-editor/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/layout/navigation-3000/navigationLogic.tsx stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/scenes/dashboard/dashboardLogic.tsx stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/lib/lemon-ui/LemonMarkdown/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/lib/components/RichContentEditor/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/lib/components/CodeSnippet/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/taxonomy/core-filter-definitions-by-group.json stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 node_modules/zod/ stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 src/scenes/billing/billingLogic.tsx stays out of src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
🟢 node_modules/monaco-editor/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/lib/components/ActivityLog/describers stays out of src/scenes/AuthenticatedShell.tsx
🟢 products/dashboards/frontend/widgets/previews/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/scenes/session-recordings/player/sessionRecordingPlayerLogic.ts stays out of src/scenes/AuthenticatedShell.tsx
🟢 zod/v4/locales/de.js stays out of src/scenes/AuthenticatedShell.tsx
🟢 node_modules/@posthog/brand/dist/generated/hoggies/svg/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 node_modules/@posthog/brand/dist/generated/hoggies/components/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/scenes/session-recordings/playlist/SessionRecordingsPlaylist.tsx stays out of src/scenes/dashboard/Dashboard.tsx
🟢 src/scenes/web-analytics/tiles/WebAnalyticsTile.tsx stays out of src/scenes/dashboard/Dashboard.tsx
🟢 src/scenes/project-homepage/ai-first/AiFirstHomepage.tsx stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
🟢 src/scenes/project-homepage/today/TodayReportPage.tsx stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
🟢 src/queries/Query/Query.tsx stays out of src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
🟢 src/scenes/session-recordings/playlist/SessionRecordingsPlaylist.tsx stays out of src/scenes/activity/explore/EventsScene.tsx
🟢 src/scenes/web-analytics/tiles/WebAnalyticsTile.tsx stays out of src/scenes/activity/explore/EventsScene.tsx

Largest files eagerly shipped from src/index.tsx
Size File
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
24.6 KiB ../node_modules/.pnpm/buffer@6.0.3/node_modules/buffer/index.js
6.3 KiB ../node_modules/.pnpm/react@18.3.1/node_modules/react/cjs/react.production.min.js
4.5 KiB ../node_modules/.pnpm/@jspm+core@2.1.0/node_modules/@jspm/core/nodelibs/browser/process.js
3.9 KiB ../node_modules/.pnpm/scheduler@0.23.2/node_modules/scheduler/cjs/scheduler.production.min.js
1.4 KiB ../node_modules/.pnpm/base64-js@1.5.1/node_modules/base64-js/index.js
1.3 KiB src/RootErrorBoundary.tsx
1.3 KiB src/index.tsx
912 B ../node_modules/.pnpm/ieee754@1.2.1/node_modules/ieee754/index.js
839 B src/scenes/ChunkLoadErrorBoundary.tsx
Largest files eagerly shipped from src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
Size File
317.7 KiB ../node_modules/.pnpm/posthog-js@1.438.4_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
220.8 KiB ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
101.5 KiB src/products.tsx
99.6 KiB src/lib/api.ts
69.0 KiB src/lib/lemon-ui/icons/icons.tsx
38.7 KiB ../node_modules/.pnpm/@dnd-kit+core@6.0.8_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@dnd-kit/core/dist/core.esm.js
33.9 KiB ../node_modules/.pnpm/kea@4.0.0-pre.6_patch_hash=139b8d1f1304f9d9da452a9a1244c94ea679dbcb85687d8999563146879fb6f5_react@18.3.1/node_modules/kea/lib/index.cjs.js
28.6 KiB src/scenes/scenes.ts
27.8 KiB src/lib/constants.tsx
Largest files eagerly shipped from src/scenes/AuthenticatedShell.tsx
Size File
317.7 KiB ../node_modules/.pnpm/posthog-js@1.438.4_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
280.8 KiB src/taxonomy/core-filter-definitions-by-group.json
220.8 KiB ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
153.7 KiB ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
113.0 KiB ../packages/quill/packages/quill/dist/index.js
101.5 KiB src/products.tsx
99.6 KiB src/lib/api.ts
93.3 KiB ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js
90.6 KiB ../node_modules/.pnpm/@tiptap+core@3.20.6_@tiptap+pm@3.20.6/node_modules/@tiptap/core/dist/index.js
Largest files eagerly shipped from src/scenes/dashboard/Dashboard.tsx
Size File
317.7 KiB ../node_modules/.pnpm/posthog-js@1.438.4_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
280.8 KiB src/taxonomy/core-filter-definitions-by-group.json
220.8 KiB ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
181.9 KiB src/queries/validators.js
153.7 KiB ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
113.0 KiB ../packages/quill/packages/quill/dist/index.js
101.5 KiB src/products.tsx
99.6 KiB src/lib/api.ts
93.3 KiB ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js
Largest files eagerly shipped from src/scenes/AuthenticatedShell.tsx + src/scenes/project-homepage/ProjectHomepage.tsx + src/scenes/project-homepage/today/TodayHome.tsx
Size File
317.7 KiB ../node_modules/.pnpm/posthog-js@1.438.4_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
280.8 KiB src/taxonomy/core-filter-definitions-by-group.json
220.8 KiB ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
153.7 KiB ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
113.0 KiB ../packages/quill/packages/quill/dist/index.js
101.5 KiB src/products.tsx
99.6 KiB src/lib/api.ts
93.3 KiB ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js
90.6 KiB ../node_modules/.pnpm/@tiptap+core@3.20.6_@tiptap+pm@3.20.6/node_modules/@tiptap/core/dist/index.js
Largest files eagerly shipped from src/scenes/activity/explore/EventsScene.tsx
Size File
317.7 KiB ../node_modules/.pnpm/posthog-js@1.438.4_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
280.8 KiB src/taxonomy/core-filter-definitions-by-group.json
220.8 KiB ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
181.9 KiB src/queries/validators.js
153.7 KiB ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
113.0 KiB ../packages/quill/packages/quill/dist/index.js
101.5 KiB src/products.tsx
99.6 KiB src/lib/api.ts
93.3 KiB ../node_modules/.pnpm/prosemirror-view@1.40.1/node_modules/prosemirror-view/dist/index.js
Largest files eagerly shipped from src/scenes/session-recordings/detail/SessionRecordingDetail.tsx
Size File
317.7 KiB ../node_modules/.pnpm/posthog-js@1.438.4_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
316.0 KiB ../node_modules/.pnpm/posthog-js@1.438.4_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/rrweb.js
280.8 KiB src/taxonomy/core-filter-definitions-by-group.json
220.8 KiB ../node_modules/.pnpm/@posthog+icons@0.38.0_react-dom@18.3.1_react@18.3.1__react@18.3.1/node_modules/@posthog/icons/dist/posthog-icons.es.js
181.9 KiB src/queries/validators.js
153.7 KiB ../node_modules/.pnpm/re2js@0.4.1/node_modules/re2js/build/index.esm.js
126.8 KiB ../node_modules/.pnpm/react-dom@18.3.1_react@18.3.1/node_modules/react-dom/cjs/react-dom.production.min.js
113.0 KiB ../packages/quill/packages/quill/dist/index.js
101.5 KiB src/products.tsx
99.6 KiB src/lib/api.ts

Posted automatically by check-eager-graph · sizes are eager output bytes (shipped, post-tree-shake) from the esbuild metafile · part of #32479

✅ Toolbar bundle — eager 2.24 MiB within budget

What the toolbar ships to customer pages, measured from the esbuild output (minified, post-tree-shake). The eager set is the entry plus everything statically imported from it — fetched before any feature runs; deferred chunks load lazily. The eager guardrail is 5.72 MiB. Each output file must also stay below 10 MB, where CloudFront stops compressing it. The module boundary is enforced separately by check-toolbar-graph.

Metric Size Δ vs base Budget
Eager (shipped)
entry + static imports
2.24 MiB · 19 files 🔺 +52 B (+0.0%) ████░░░░░░ 39.2% of 5.72 MiB
Deferred (lazy) 2.19 MiB · 44 files no change n/a — loads on demand
Loader dist/toolbar.js 1.2 KiB no change █░░░░░░░░░ 6.0% of 19.5 KiB
Largest eagerly-shipped chunks
Size File
872.0 KiB dist/toolbar/toolbar-app-3HUDB65W.css
662.1 KiB dist/toolbar/chunk-chunk-JILBSQBP.js
259.6 KiB dist/toolbar/chunk-chunk-LPBBQMMO.js
138.9 KiB dist/toolbar/chunk-chunk-KET4TR67.js
131.8 KiB dist/toolbar/chunk-chunk-FDH2IBXT.js
75.2 KiB dist/toolbar/toolbar-app-HNBB3BCN.js
69.0 KiB dist/toolbar/chunk-chunk-TSAL54PB.js
35.6 KiB dist/toolbar/chunk-chunk-PGCMOZZH.js
21.7 KiB dist/toolbar/chunk-chunk-KZRXAZLE.js
6.8 KiB dist/toolbar/chunk-chunk-DV7IWQNF.js

Posted automatically by check-toolbar-size · sizes are toolbar output bytes (shipped, post-tree-shake) from the esbuild metafile

✅ Dist folder size — 🔺 +5.3 KiB (+0.0%)

Total size of the built frontend/dist folder (all assets), compared against the base branch.

Total: 1010.88 MiB · 🔺 +5.3 KiB (+0.0%)

✅ Playwright — all passed

All tests passed.

View test results →

⚠️ Backend coverage — 99.0% of changed backend lines covered — 1 uncovered

🧪 Backend test coverage

Patch coverage — changed backend lines (products + core): ████████████████████ 99.0% (248 / 249)

File Patch Uncovered changed lines
products/workflows/backend/presentation/views/workflow_classifications.py 99.2% 70

🤖 Agents: add a test only if an uncovered line exposes a realistic regression that existing tests miss. Otherwise explain why no new test is needed under "How did you test this code?". Gap list: the patch-coverage artifact on this run (gh run download 459639349083739 -n patch-coverage), or the coverage-data block at the end of this comment.

Per-product line coverage (touched products)
Product Coverage Lines
platform_features ██░░░░░░░░░░░░░░░░░░ 12.1% 7 / 58
demo ███████████░░░░░░░░░ 55.9% 1,508 / 2,699
data_tools ████████████░░░░░░░░ 61.2% 90 / 147
warehouse_sources_queue █████████████░░░░░░░ 66.8% 1,922 / 2,878
aeo ███████████████░░░░░ 76.3% 617 / 809
batch_exports ████████████████░░░░ 80.1% 21,422 / 26,728
apm █████████████████░░░ 84.1% 1,306 / 1,553
engineering_analytics █████████████████░░░ 84.9% 11,355 / 13,374
mcp_analytics █████████████████░░░ 85.7% 4,844 / 5,651
ml_inference █████████████████░░░ 86.3% 524 / 607
warehouse_suggestions █████████████████░░░ 87.3% 2,252 / 2,580
posthog_ai ██████████████████░░ 87.7% 4,226 / 4,817
notebooks ██████████████████░░ 88.4% 15,201 / 17,196
today ██████████████████░░ 88.6% 2,866 / 3,233
cdp ██████████████████░░ 88.7% 5,081 / 5,729
mcp_registry ██████████████████░░ 89.0% 1,596 / 1,794
product_tours ██████████████████░░ 89.1% 1,337 / 1,500
dashboards ██████████████████░░ 89.4% 7,322 / 8,194
webmcp ██████████████████░░ 89.6% 240 / 268
cohorts ██████████████████░░ 89.6% 8,458 / 9,438
signals ██████████████████░░ 90.0% 69,561 / 77,329
data_modeling ██████████████████░░ 90.1% 10,856 / 12,043
ai_training ██████████████████░░ 90.4% 349 / 386
tasks ██████████████████░░ 90.6% 80,754 / 89,126
visual_review ██████████████████░░ 90.7% 9,842 / 10,856
data_warehouse ██████████████████░░ 90.8% 14,831 / 16,339
canvas ██████████████████░░ 91.0% 7,804 / 8,580
exports ██████████████████░░ 91.0% 9,764 / 10,732
business_knowledge ██████████████████░░ 91.0% 8,791 / 9,656
autoresearch ██████████████████░░ 91.1% 10,436 / 11,453
managed_warehouse ██████████████████░░ 91.2% 11,053 / 12,114
error_tracking ██████████████████░░ 91.6% 16,797 / 18,328
wizard ██████████████████░░ 91.8% 6,008 / 6,548
streamlit_apps ██████████████████░░ 91.9% 3,139 / 3,415
stamphog ██████████████████░░ 92.0% 8,243 / 8,964
conversations ██████████████████░░ 92.0% 29,714 / 32,284
early_access_features ███████████████████░ 92.6% 1,339 / 1,446
managed_migrations ███████████████████░ 92.6% 1,630 / 1,760
alerts ███████████████████░ 92.9% 7,246 / 7,797
surveys ███████████████████░ 93.1% 6,626 / 7,118
web_analytics ███████████████████░ 93.1% 24,096 / 25,877
review_hog ███████████████████░ 93.1% 15,855 / 17,026
notifications ███████████████████░ 93.2% 1,152 / 1,236
approvals ███████████████████░ 93.4% 4,526 / 4,848
cross_project_dashboards ███████████████████░ 93.4% 880 / 942
slack_app ███████████████████░ 93.5% 14,854 / 15,882
context_layer ███████████████████░ 93.7% 3,409 / 3,640
marketing_analytics ███████████████████░ 93.8% 20,074 / 21,402
messaging ███████████████████░ 93.8% 4,468 / 4,762
workflows ███████████████████░ 93.9% 17,287 / 18,413
billing_alerts ███████████████████░ 93.9% 2,090 / 2,226
customer_analytics ███████████████████░ 93.9% 26,079 / 27,773
mcp_store ███████████████████░ 93.9% 9,008 / 9,593
experiments ███████████████████░ 94.1% 33,981 / 36,101
ai_observability ███████████████████░ 94.2% 26,434 / 28,069
replay_vision ███████████████████░ 94.3% 30,070 / 31,884
legal_documents ███████████████████░ 94.3% 2,289 / 2,427
actions ███████████████████░ 94.6% 973 / 1,029
reminders ███████████████████░ 94.8% 760 / 802
endpoints ███████████████████░ 94.8% 9,333 / 9,847
skills ███████████████████░ 94.8% 7,061 / 7,445
growth ███████████████████░ 94.8% 11,268 / 11,880
metrics ███████████████████░ 95.0% 6,733 / 7,089
annotations ███████████████████░ 95.0% 816 / 859
tracing ███████████████████░ 95.2% 3,631 / 3,815
data_catalog ███████████████████░ 95.5% 4,419 / 4,628
access_control ███████████████████░ 95.6% 7,881 / 8,245
product_analytics ███████████████████░ 95.7% 28,723 / 29,999
alerts_platform ███████████████████░ 95.8% 5,012 / 5,234
revenue_analytics ███████████████████░ 95.9% 1,878 / 1,959
data_quality ███████████████████░ 95.9% 8,569 / 8,933
feature_flags ███████████████████░ 96.2% 26,932 / 27,997
warehouse_sources ███████████████████░ 96.4% 458,272 / 475,397
security ███████████████████░ 96.7% 1,452 / 1,501
pulse ███████████████████░ 97.4% 2,023 / 2,078
analytics_platform ████████████████████ 98.0% 2,775 / 2,833
field_notes ████████████████████ 99.4% 172 / 173

Report-only. Patch coverage = changed backend lines covered vs origin/master. Sorted lowest first.
Known gaps: lines covered only by Temporal tests show as uncovered; core line numbers may drift if master changed the same file.

✅ Hogbox preview — ready, open the preview

▶ Open the preview

🔑 Login test@posthog.com / 12345678 (demo data)
🧩 Running this PR's backend and frontend, on the PostHog :master base
🔗 Link stable across rebuilds: a re-push swaps the box underneath, the URL stays
🔒 Access tailnet only (PostHog VPN)
🛠️ Admin inspect & debug state in hogland
💤 Idle sleeps after ~30 min idle (snapshot to S3, zero node cost) and wakes on your next visit in ~30s, behind a brief "waking up" screen

commit 611fb3d · box box-d058e5996aca · ready in 1460s (push → usable) · build log · rebuilds on every push, torn down on close

@posthog

posthog Bot commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

PostHog Review alpha 🦔 If you find any issues helpful - please reply "valid", "invalid", etc., for evaluation purposes 🙏

@posthog posthog Bot removed the reviewhog label Oct 8, 2026
@trunk-io

trunk-io Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

Static Badge   Static Badge   Static Badge

Failed Test Failure Summary Logs
Scenes-App/Project Homepage ProjectHomepage smoke-test The test timed out while waiting for loading indicators or spinners to disappear. Logs ↗︎
Customer Analytics/Account views/Session replays Loading smoke-test The test timed out while waiting for the element with data attribute 'account-replays-loading' to become visible. Logs ↗︎
personalAPIKeysLogic clamps a group write to what each row can take The test exceeded the maximum allowed time of 5000 ms and did not complete. Logs ↗︎
Event Pipeline E2E tests (fold+groupBatchCreates=true) we do not leave things in inconsistent state if $identify is run concurrently A test failed because the expected value was 2, but the actual received value was 1. Logs ↗︎

View Full Report ↗︎ ⋅ Docs

@mayteio mayteio changed the title feat(workflows): add a classify with jev workflow action feat(workflows): add the classify with jev endpoint and builder entry Oct 8, 2026
@mayteio
mayteio added this pull request to stack #114096 October 8, 2026 17:23
@mayteio
mayteio marked this pull request as ready for review October 9, 2026 08:41
@mayteio
mayteio requested a review from a team as a code owner October 9, 2026 08:41
@parameterai

parameterai Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

Risk: No findings

This delta comments out the GPT-6 Luna model from the Classify with AI step on both sides: the backend allowlist (CLASSIFICATION_MODELS) and the template's model picker. That closes the previously reported path sending customer workflow context to OpenAI before vendor sign-off; the serializer now rejects gpt-6-luna with a 400 before any model call, covered by a new test. No new security issues are introduced by these changes.

Sentinel reviewed 611fb3d · Review settings

@mayteio mayteio added the stamphog Request AI approval (no full review) label Oct 9, 2026 — with PostHog
@pr-assigner-resolver-posthog

pr-assigner-resolver-posthog Bot commented Oct 9, 2026 •

Copy link
Copy Markdown

👀 Auto-assigned reviewers

These soft owners were skipped because they only have minor changes here. Nothing blocks merge, so self-assign if you'd like a look:

  • @PostHog/team-devex (owners.yaml)
  • @PostHog/team-ingestion (owners.yaml)

Soft owners come from each directory's owners.yaml and each product's product.yaml (resolved nearest-file-wins). For a skipped owner, the locator is the file that decided it. Generated files and lockfiles are ignored when deciding ownership.

stamphog[bot]

This comment was marked as outdated.

@stamphog stamphog Bot removed the stamphog Request AI approval (no full review) label Oct 9, 2026
@mayteio mayteio added the cross-lane-change Allow for cross lane (i.e. django + rust) changes to merge at once label Oct 9, 2026 — with PostHog
@mayteio
mayteio removed this pull request from stack #114096 October 9, 2026 10:50
@mayteio mayteio changed the title feat(workflows): add the classify with jev endpoint and builder entry feat(workflows): add a classify with jev workflow action Oct 9, 2026
@mayteio mayteio added the run-ci-frontend Force ci-storybook's full visual-regression matrix to run even on a draft PR label Oct 9, 2026 — with PostHog
@mayteio
mayteio marked this pull request as draft October 9, 2026 11:37
@mayteio
mayteio marked this pull request as ready for review October 9, 2026 11:37
@mayteio mayteio changed the title feat(workflows): add a classify with jev workflow action feat(workflows): add a classify with ai workflow action Oct 9, 2026
@mayteio
mayteio force-pushed the posthog/workflows-classify-with-jev branch from 92c583a to e0c196a Compare October 12, 2026 10:32
Revives the "Classify with Jev" step from #107832 and #111710 on current master.

The classification endpoint asks the model through the ml_inference facade with privacy mode on, and rejects answers it cannot branch on. The step and its endpoint stay behind the workflow-jev-classify-action flag.

Generated-By: PostHog Desktop
Task-Id: 8ed258e7-0201-436d-9625-5743d7c23174
A context nested about 255 levels deep passed the request serializer, then failed DecisionRequest validation. The validation error repeated the whole context, and the view returned a 500 that logs and exception reporting captured. The worker retried it.

The serializer now rejects a context nested more than 100 levels deep with a fixed 400 message. The worker's matching checks apply the same limit, so a mocked test run fails on the same input as a live run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

Generated-By: PostHog Desktop
Task-Id: 8ff3bc0d-a415-4934-8bcb-3f1ae6818e4d
Jev is TypeSafe's decision model, so the step now shows TypeSafe's mark instead of the generic PostHog icon in the palette, on the canvas node and in the step panel.

Generated-By: PostHog Desktop
Task-Id: 8ed258e7-0201-436d-9625-5743d7c23174
The gateway timeout in the classification endpoint limits each network operation, not the whole call, so Django can overrun the worker's 7 second budget. Retrying that timeout sent the same paid classification to the gateway again while the first one could still be answering. callInternalApi gets a retryOnTimeout option, on by default, and the classify step turns it off. Refused connections, 503s and 429s still retry. The timeout check moves to utils/cdp-fetch.ts so the batch person query and the internal API call share it.

Generated-By: PostHog Desktop
Task-Id: 8ed258e7-0201-436d-9625-5743d7c23174
Prettier wrapped the jwt.verify call, which moved the dev-default secret below the line the nosemgrep comment covers. Pull the token into a variable, like the create task and run scout tests, so the literal stays on the suppressed line.

Generated-By: PostHog Desktop
Task-Id: 8ed258e7-0201-436d-9625-5743d7c23174
mypy rejected the reduce that builds the deeply nested context, because the string seed fixed its type. Seed it as Any.

Generated-By: PostHog Desktop
Task-Id: 8ed258e7-0201-436d-9625-5743d7c23174
The merge queue keeps Python and frontend changes in a separate lane from Node changes. This PR keeps the Django endpoint, save-time validation and builder entry. The CDP worker function, the step template and the internal API retry change move to the PR stacked on top.

Generated-By: PostHog Desktop
Task-Id: 8ed258e7-0201-436d-9625-5743d7c23174
The workflow-claim check now returns the claims unchanged, with the base class signature, so the tuple-return rule needs no exemption. The view reads hog_flow_id from the claims.

Generated-By: PostHog Desktop
Task-Id: 8ed258e7-0201-436d-9625-5743d7c23174
Folds the worker back in from the stacked PR. Adds the postHogClassify async function and the step template, and stops the worker retrying its own timeout.

Generated-By: PostHog Desktop
Task-Id: 8ed258e7-0201-436d-9625-5743d7c23174
…th ai

Renames the step to "Classify with AI" before dogfooding saves its template id and variable names into workflows. The step gets a per-step model picker, defaulting to Jev. The endpoint accepts only models on an allowlist, which holds only Jev for now.

The template id is now template-posthog-classify, the output variables are category and confidence, and the flag is workflow-classify-action.

Generated-By: PostHog Desktop
Task-Id: 8ed258e7-0201-436d-9625-5743d7c23174
… with ai

The step is model-agnostic now, so it shows the PostHog icon instead of the TypeSafe logo. The model choices come from a TextChoices class, as the choices-need-a-class rule requires.

Generated-By: PostHog Desktop
Task-Id: 8ed258e7-0201-436d-9625-5743d7c23174
The AI gateway routes openai/gpt-6-luna on /v1/systemone, so the classify step can offer it next to Jev. Jev stays the default.

Generated-By: PostHog Desktop
Task-Id: b2514852-f00d-4902-96dc-4fa858c4f461
A team id repeats across PostHog instances, so a flag condition on the project id also matches project 2 on self-hosted and local dev instances. Passing the uuid lets a flag target one project.

Generated-By: PostHog Desktop
Task-Id: b2514852-f00d-4902-96dc-4fa858c4f461
@mayteio
mayteio force-pushed the posthog/workflows-classify-with-jev branch from 450ec07 to 52491d4 Compare October 12, 2026 14:59
Master removed the beta tags from the workflow builder nodes and their LemonTag import, so the classify node no longer type-checks with its tag.

Generated-By: PostHog Desktop
Task-Id: b2514852-f00d-4902-96dc-4fa858c4f461
@github-actions
github-actions Bot requested a deployment to preview-pr-114006 October 12, 2026 14:59 In progress
OpenAI's processing of workflow context is not signed off yet, so the picker and the endpoint offer Jev only. The GPT-6 Luna entries stay commented out for a later enable.

Generated-By: PostHog Desktop
Task-Id: b2514852-f00d-4902-96dc-4fa858c4f461

mayteio commented Oct 12, 2026

Copy link
Copy Markdown
Contributor Author

/trunk merge

@trunk-io
trunk-io Bot merged commit 9c2dd2a into master Oct 12, 2026
@trunk-io
trunk-io Bot deleted the posthog/workflows-classify-with-jev branch October 12, 2026 17:48
@deployment-status-posthog

deployment-status-posthog Bot commented Oct 12, 2026 •

Copy link
Copy Markdown

Deploy status

Environment Status Deployed At Workflow
dev ✅ Deployed 2026-10-12 18:09 UTC Run
prod-us ✅ Deployed 2026-10-12 18:33 UTC Run
prod-eu ✅ Deployed 2026-10-12 18:33 UTC Run

This branch was successfully deployed

1 active deployment
preview-pr-114006 — 611fb3da Deployed Oct 12, 2026 by github-actions[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

cross-lane-change Allow for cross lane (i.e. django + rust) changes to merge at once run-ci-frontend Force ci-storybook's full visual-regression matrix to run even on a draft PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants