feat(growth): turn suggestion backend - #106432
rafaeelaudibert wants to merge 26 commits into
Conversation
🤖 CI report
|
| Root | Eager (shipped) | Δ vs base | Budget |
|---|---|---|---|
entry (logged-out pages, app bootstrap)src/index.tsx |
1.57 MiB · 22 files | no change | █████████░ 85.1% of 1.84 MiB |
logged-out boot: index + App + bootApp (preloaded by every page, including /login)src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts |
3.57 MiB · 628 files | no change | █████████░ 88.6% of 4.03 MiB |
authenticated shell (every logged-in page)src/scenes/AuthenticatedShell.tsx |
7.27 MiB · 2,301 files | no change | █████████░ 87.2% of 8.34 MiB |
🟢 node_modules/monaco-editor/ stays out of src/index.tsx
🟢 src/lib/components/ActivityLog/describers stays out of src/index.tsx
🟢 [object Object] stays out of src/index.tsx
🟢 [object Object] 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/monaco-editor/ stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/lib/components/ActivityLog/describers stays out of src/scenes/AuthenticatedShell.tsx
🟢 [object Object] stays out of src/scenes/AuthenticatedShell.tsx
🟢 src/scenes/session-recordings/player/sessionRecordingPlayerLogic.ts stays out of src/scenes/AuthenticatedShell.tsx
🟢 [object Object] stays out of src/scenes/AuthenticatedShell.tsx
🟢 [object Object] stays out of src/scenes/AuthenticatedShell.tsx
🟢 [object Object] stays out of src/scenes/AuthenticatedShell.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/index.tsx |
| 1.3 KiB | src/RootErrorBoundary.tsx |
| 912 B | ../node_modules/.pnpm/ieee754@1.2.1/node_modules/ieee754/index.js |
| 854 B | src/scenes/ChunkLoadErrorBoundary.tsx |
Largest files eagerly shipped from src/index.tsx + src/scenes/App.tsx + src/scenes/bootApp.ts
| Size | File |
|---|---|
| 301.8 KiB | ../node_modules/.pnpm/posthog-js@1.434.13_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs |
| 267.6 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 |
| 100.4 KiB | src/lib/api.ts |
| 84.9 KiB | src/products.tsx |
| 69.1 KiB | src/lib/lemon-ui/icons/icons.tsx |
| 63.9 KiB | src/lib/utils/eventUsageLogic.ts |
| 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.3 KiB | src/scenes/scenes.ts |
Largest files eagerly shipped from src/scenes/AuthenticatedShell.tsx
| Size | File |
|---|---|
| 301.8 KiB | ../node_modules/.pnpm/posthog-js@1.434.13_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs |
| 271.7 KiB | src/taxonomy/core-filter-definitions-by-group.json |
| 267.6 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 |
| 100.4 KiB | src/lib/api.ts |
| 98.5 KiB | ../packages/quill/packages/quill/dist/index.js |
| 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 |
| 84.9 KiB | src/products.tsx |
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.37 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.37 MiB · 19 files | no change | ████░░░░░░ 41.4% of 5.72 MiB |
| Deferred (lazy) | 2.10 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 |
|---|---|
| 790.2 KiB | dist/toolbar/toolbar-app-ZNXS4H6W.css |
| 650.4 KiB | dist/toolbar/chunk-chunk-DPTOFHTZ.js |
| 483.6 KiB | dist/toolbar/chunk-chunk-DPLA2GAY.js |
| 138.3 KiB | dist/toolbar/chunk-chunk-2EHPTPPA.js |
| 131.8 KiB | dist/toolbar/chunk-chunk-FDH2IBXT.js |
| 75.2 KiB | dist/toolbar/toolbar-app-FOEGQ3V3.js |
| 69.0 KiB | dist/toolbar/chunk-chunk-TSAL54PB.js |
| 35.6 KiB | dist/toolbar/chunk-chunk-U5CQIMDB.js |
| 21.0 KiB | dist/toolbar/chunk-chunk-OYNHVBHM.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 — 🔺 +7.7 KiB (+0.0%)
Total size of the built frontend/dist folder (all assets), compared against the base branch.
Total: 943.53 MiB · 🔺 +7.7 KiB (+0.0%)
ℹ️ MCP UI apps size — 33 app(s), 17628.2 KB JS
Built size of each MCP UI app (main.js + styles.css).
| App | JS | CSS |
|---|---|---|
| debug | 597.8 KB | 196.2 KB |
| action | 454.1 KB | 196.2 KB |
| action-list | 564.1 KB | 196.2 KB |
| cohort | 453.1 KB | 196.2 KB |
| cohort-list | 563.1 KB | 196.2 KB |
| email-template | 452.9 KB | 196.2 KB |
| error-details | 469.1 KB | 196.2 KB |
| error-issue | 453.8 KB | 196.2 KB |
| error-issue-list | 564.0 KB | 196.2 KB |
| experiment | 561.2 KB | 196.2 KB |
| experiment-list | 564.9 KB | 196.2 KB |
| experiment-results | 566.2 KB | 196.2 KB |
| feature-flag | 566.7 KB | 196.2 KB |
| feature-flag-list | 570.5 KB | 196.2 KB |
| feature-flag-testing | 457.3 KB | 196.2 KB |
| inline-scan | 453.6 KB | 196.2 KB |
| insight-actors | 562.3 KB | 196.2 KB |
| invite-email-preview | 452.3 KB | 196.2 KB |
| llm-costs | 559.3 KB | 196.2 KB |
| session-recording | 454.9 KB | 196.2 KB |
| survey | 454.7 KB | 196.2 KB |
| survey-global-stats | 561.8 KB | 196.2 KB |
| survey-list | 564.8 KB | 196.2 KB |
| survey-stats | 561.8 KB | 196.2 KB |
| trace-span | 453.5 KB | 196.2 KB |
| trace-span-list | 564.0 KB | 196.2 KB |
| vision-observation-list | 563.2 KB | 196.2 KB |
| workflow | 453.4 KB | 196.2 KB |
| workflow-list | 563.5 KB | 196.2 KB |
| loops-review | 457.8 KB | 196.2 KB |
| query-results | 774.0 KB | 196.2 KB |
| render-ui | 856.5 KB | 196.2 KB |
| visual-review-snapshots | 457.9 KB | 196.2 KB |
|
[High risk] Adds a new background job queue and task processing system. The PR is not safe to merge: Luna drafts fail, several valid turns cannot produce cards, and the judge sends customer-project text across an unapproved data boundary. Reviews (1) · Last reviewed commit: "feat(growth): turn suggestion backend" |
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. Important Review skippedWe couldn't safely recover the incremental review. No full review was started, and the last reviewed checkpoint was preserved. Retry later, or explicitly request a full review by commenting You can disable this status message by setting the Use the checkbox below for a quick retry:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository: PostHog/posthog/.coderabbit.yaml Review profile: QUIET Plan: Enterprise Run ID: ⛔ Files ignored due to path filters (3)
📒 Files selected for processing (22)
💤 Files with no reviewable changes (1)
Included review availability: Your plan provides up to 12 included reviews per hour; 1 remains after this review. 📝 WalkthroughWalkthroughThe change adds PostHog AI turn suggestions. It extracts and redacts conversation transcripts, judges turns, selects and drafts offers, and records and publishes suggestions. Completed task runs can trigger delayed generation through a new Celery queue. Task-scoped API endpoints return suggestion state and record resolutions. MCP tool definitions, generated API types, and tests are also added. Priority: ⬇️ Low Merge Risk: 🔵 Low · up to This adds turn suggestion processing for PostHog AI behind a flag that is off by default. Earlier concerns about a crash on large turns and a type-check failure appear to be addressed, but neither fix has been confirmed at the current head. Before enabling the feature, confirm that CI passes, that the posthog_ai worker runs in each region, and that the TypeSafe fallback does not send customer text. Security Architecture ReviewSecurity architecture risk: 🟡 Moderate · up to Access checks and consent checks are present, but an interrupted background operation can leave a suggestion recorded without displaying it. The new endpoints and worker path also depend on access enforcement and regional deployment conditions that are not fully verified here. Retained concerns
Security review detailsSecurity Blast Radius
Security Findings and Attack Paths
Trust Boundaries and Controls
Resilience and Maintainability Implications
Hardening Proposals
🚥 Pre-merge checks | ✅ 1✅ Passed checks (1 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
Note
Quiet mode is enabled, so only the most important comments were posted inline. Other review comments are grouped below.
🟡 Other comments (1)
products/posthog_ai/backend/turn_suggestions/judgment.py-205-214 (1)
205-214: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick winCap the insight and issue options. Otherwise a large turn crashes the task.
_issue_optionsand_insight_optionsnumber every ref in the transcript._error_issuesintranscript.pycollects every row from everyquery-error-tracking-issues-listresult in the turn. For example, a turn with a few 100-row list calls can yield more than 254 distinct issues. With the addednoneoption,ChoiceQuestion.__post_init__then raisesValueErrorbecause the count exceedsMAX_CHOICE_OPTIONS.
build_judge_questionsruns inside thetryinjudge_turn, but theexceptclauses catch only the TypeSafe errors andOSError. TheValueErrortherefore propagates out ofgenerate_turn_suggestion_task.claim_turnhas already run at that point, so the turn is consumed without a recorded outcome or analytics event.Cap the refs in one place.
build_judge_stateandread_judgmentuse the same helpers, so the state, the questions and the answer lookup stay in sync.Proposed fix
+# One option stays free for `_NO_MATCH`; see MAX_CHOICE_OPTIONS in posthog.egress.typesafe. +_MAX_REF_OPTIONS = 50 + def _numbered[T](prefix: str, refs: Sequence[T]) -> dict[str, T]: - return {f"{prefix}_{index}": ref for index, ref in enumerate(refs, start=1)} + return {f"{prefix}_{index}": ref for index, ref in enumerate(refs[:_MAX_REF_OPTIONS], start=1)}
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository: PostHog/posthog/.coderabbit.yaml
Review profile: QUIET
Plan: Enterprise
Run ID: 3f5b4fd9-99ea-4812-8e43-0c9d4db24213
⛔ Files ignored due to path filters (3)
products/posthog_ai/frontend/generated/api.schemas.tsis excluded by!**/generated/**products/posthog_ai/frontend/generated/api.tsis excluded by!**/generated/**products/posthog_ai/frontend/generated/api.zod.tsis excluded by!**/generated/**
📒 Files selected for processing (22)
bin/celery-queues.envposthog/celery_queues.pyposthog/egress/typesafe/README.mdproducts/posthog_ai/backend/api/__init__.pyproducts/posthog_ai/backend/api/turn_suggestions.pyproducts/posthog_ai/backend/receivers.pyproducts/posthog_ai/backend/routes.pyproducts/posthog_ai/backend/tasks.pyproducts/posthog_ai/backend/tests/test_turn_suggestions.pyproducts/posthog_ai/backend/tests/test_turn_suggestions_api.pyproducts/posthog_ai/backend/turn_suggestions/__init__.pyproducts/posthog_ai/backend/turn_suggestions/classifier.pyproducts/posthog_ai/backend/turn_suggestions/dispatch.pyproducts/posthog_ai/backend/turn_suggestions/drafter.pyproducts/posthog_ai/backend/turn_suggestions/judgment.pyproducts/posthog_ai/backend/turn_suggestions/offer_ledger.pyproducts/posthog_ai/backend/turn_suggestions/service.pyproducts/posthog_ai/backend/turn_suggestions/transcript.pyproducts/posthog_ai/backend/turn_suggestions/verdict.pyproducts/posthog_ai/mcp/tools.yamlservices/mcp/src/api/generated.tstach.toml
Included review availability: Your plan provides up to 12 included reviews per hour; 5 remain after this review.
b01e9e0 to
24076a6
Compare
24076a6 to
bae2470
Compare
|
@coderabbitai review-body comment:
|
|
Tip For best results, initiate chat on the files or code changes. 🧩 Analysis chain🏁 Script executed: #!/bin/bash
set -euo pipefail
git show --stat --oneline bae2470ae09
git show --format=fuller --no-ext-diff bae2470ae09 -- products/posthog_ai/backend/turn_suggestions/judgment.py products/posthog_ai/backend/turn_suggestions/transcript.py
ast-grep outline products/posthog_ai/backend/turn_suggestions/judgment.py --match 'build_judge_state|build_judge_questions|read_judgment'
rg -n -C 4 'saved_insights|error_issues|MAX_.*OPTIONS|insight_options|issue_options' products/posthog_ai/backend/turn_suggestions/judgment.py products/posthog_ai/backend/turn_suggestions/transcript.pyLength of output: 23649
You are interacting with an AI system. |
There was a problem hiding this comment.
Actionable comments posted: 1
Note
Quiet mode is enabled, so only the most important comments were posted inline. Other review comments are grouped below.
🟡 Other comments (1)
products/posthog_ai/backend/turn_suggestions/transcript.py-254-262 (1)
254-262: 🎯 Functional Correctness | 🟡 Minor | ⚡ Quick winAssemble streamed
user_message_chunkframes before starting a turn.ACP permits one user message to arrive as multiple
user_message_chunkupdates with the samemessageId. The backend currently callsstart_turnfor every chunk, and exact-text deduplication cannot match a full_posthog/user_messageagainst an individual chunk. The frontend parser also emits one user item per chunk without assemblingmessageIdgroups.Accumulate chunks by
messageIdbefore callingstart_turn, then apply the existing deduplication to the assembled message.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository: PostHog/posthog/.coderabbit.yaml
Review profile: QUIET
Plan: Enterprise
Run ID: 4aef9b4b-d343-4e6f-b973-1d6b14722fc4
⛔ Files ignored due to path filters (3)
products/posthog_ai/frontend/generated/api.schemas.tsis excluded by!**/generated/**products/posthog_ai/frontend/generated/api.tsis excluded by!**/generated/**products/posthog_ai/frontend/generated/api.zod.tsis excluded by!**/generated/**
📒 Files selected for processing (22)
bin/celery-queues.envposthog/celery_queues.pyposthog/egress/typesafe/README.mdproducts/posthog_ai/backend/api/__init__.pyproducts/posthog_ai/backend/api/turn_suggestions.pyproducts/posthog_ai/backend/receivers.pyproducts/posthog_ai/backend/routes.pyproducts/posthog_ai/backend/tasks.pyproducts/posthog_ai/backend/tests/test_turn_suggestions.pyproducts/posthog_ai/backend/tests/test_turn_suggestions_api.pyproducts/posthog_ai/backend/turn_suggestions/__init__.pyproducts/posthog_ai/backend/turn_suggestions/classifier.pyproducts/posthog_ai/backend/turn_suggestions/dispatch.pyproducts/posthog_ai/backend/turn_suggestions/drafter.pyproducts/posthog_ai/backend/turn_suggestions/judgment.pyproducts/posthog_ai/backend/turn_suggestions/offer_ledger.pyproducts/posthog_ai/backend/turn_suggestions/service.pyproducts/posthog_ai/backend/turn_suggestions/transcript.pyproducts/posthog_ai/backend/turn_suggestions/verdict.pyproducts/posthog_ai/mcp/tools.yamlservices/mcp/src/api/generated.tstach.toml
💤 Files with no reviewable changes (1)
- products/posthog_ai/backend/turn_suggestions/init.py
Included review availability: Your plan provides up to 12 included reviews per hour; 4 remain after this review.
4bfafe9 to
662d586
Compare
662d586 to
db6d16b
Compare
db6d16b to
742e8b8
Compare
|
@coderabbitai review-body comment:
|
There was a problem hiding this comment.
Actionable comments posted: 1
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository: PostHog/posthog/.coderabbit.yaml
Review profile: QUIET
Plan: Enterprise
Run ID: 49f1298d-483b-4aa2-b1b7-9e9c72370935
⛔ Files ignored due to path filters (3)
products/posthog_ai/frontend/generated/api.schemas.tsis excluded by!**/generated/**products/posthog_ai/frontend/generated/api.tsis excluded by!**/generated/**products/posthog_ai/frontend/generated/api.zod.tsis excluded by!**/generated/**
📒 Files selected for processing (22)
bin/celery-queues.envposthog/celery_queues.pyposthog/egress/typesafe/README.mdproducts/posthog_ai/backend/api/__init__.pyproducts/posthog_ai/backend/api/turn_suggestions.pyproducts/posthog_ai/backend/receivers.pyproducts/posthog_ai/backend/routes.pyproducts/posthog_ai/backend/tasks.pyproducts/posthog_ai/backend/tests/test_turn_suggestions.pyproducts/posthog_ai/backend/tests/test_turn_suggestions_api.pyproducts/posthog_ai/backend/turn_suggestions/__init__.pyproducts/posthog_ai/backend/turn_suggestions/classifier.pyproducts/posthog_ai/backend/turn_suggestions/dispatch.pyproducts/posthog_ai/backend/turn_suggestions/drafter.pyproducts/posthog_ai/backend/turn_suggestions/judgment.pyproducts/posthog_ai/backend/turn_suggestions/offer_ledger.pyproducts/posthog_ai/backend/turn_suggestions/service.pyproducts/posthog_ai/backend/turn_suggestions/transcript.pyproducts/posthog_ai/backend/turn_suggestions/verdict.pyproducts/posthog_ai/mcp/tools.yamlservices/mcp/src/api/generated.tstach.toml
💤 Files with no reviewable changes (1)
- products/posthog_ai/backend/turn_suggestions/init.py
Included review availability: Your plan provides up to 12 included reviews per hour; 6 remain after this review.
|
|
||
| | Caller | Source | Lane | Feature flag | | ||
| | ---------------------------------------------------------------------------------------- | ----------------------------- | ------- | ----------------------------- | | ||
| | PostHog AI turn suggestions (`products/posthog_ai/backend/turn_suggestions/judgment.py`) | `posthog_ai_turn_suggestions` | `BATCH` | `posthog-ai-turn-suggestions` | |
There was a problem hiding this comment.
🔒 Security & Privacy | 🛡️ Detected with Advanced Tier | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
sed -n '45,65p' posthog/egress/typesafe/README.md
sed -n '210,340p' products/posthog_ai/backend/turn_suggestions/judgment.py
sed -n '155,215p' products/posthog_ai/backend/turn_suggestions/service.py
rg -n 'def build_system_one_client|class TypeSafeFallback|approved for|customer data' posthog/llm/system_one.py posthog/egress/typesafe/README.mdRepository: PostHog/posthog
Length of output: 11630
🏁 Script executed:
#!/bin/bash
set -eu
printf '%s\n' '--- system_one definitions and callers ---'
rg -n -A70 -B20 'def build_system_one_client|class TypeSafeFallback|TypeSafeFallback|system_one_configured|JUDGE_TYPESAFE_FALLBACK|JUDGE_MODEL|AI_GATEWAY_URL|AI_GATEWAY_API_KEY' posthog/llm products/posthog_ai/backend/turn_suggestions posthog/egress/typesafe
printf '%s\n' '--- TypeSafe policy and configuration ---'
sed -n '1,45p' posthog/egress/typesafe/README.md
rg -n -A25 -B15 'is_ai_data_processing_approved|posthog-ai-turn-suggestions|turn_suggestions_enabled|def _turn_suggestions_enabled|generate_turn_suggestion' products/posthog_ai/backend/turn_suggestions
printf '%s\n' '--- PR diff for relevant changed files ---'
git diff --unified=25 71a423bdd4e9b3315fa1953f1e63c18b254590db 742e8b8e7e2f794a545ce5ddf76fdeccb76f4af2 -- products/posthog_ai/backend/turn_suggestions/judgment.py products/posthog_ai/backend/turn_suggestions/service.py posthog/llm posthog/egress/typesafe/README.mdRepository: PostHog/posthog
Length of output: 43305
Sensitive Data Exposure
Reachability: External
Exploitability: Difficult
CWE: CWE-200 — Exposure of Sensitive Information to an Unauthorized Actor
Sensitive Data Exposure
Reachability: External
Exploitability: Difficult
CWE: CWE-200 — Exposure of Sensitive Information to an Unauthorized Actor
Exclude customer turn text from the TypeSafe fallback. The judgment state includes raw user questions, and the TypeSafe fallback sends that state to TypeSafe when the gateway is unavailable. The general is_ai_data_processing_approved check and feature flag do not establish the TypeSafe-specific customer opt-in and leadership sign-off required by the TypeSafe policy. Keep customer text out of the TypeSafe request, or add the required authorization before enabling this fallback.
The not-configured banner opened Slack's OAuth in the same tab, so any form the user was filling in was lost. It now opens a new tab, polls integrations until the new workspace appears, and hands it to the caller through onConnected. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
A workspace another project member adds while the banner waits is no longer picked up, and a click before the first integrations load takes the next successful load as the baseline instead of an empty list. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…e connect link Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…rkspaces Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…lpers The three turn-completed reporters share one fan-out that sends a task_run_turn_completed signal, so other products react to a finished turn without the tasks product importing them. The facade gains a single-lookup conversation history read, task state helpers on a new Task.mutate_state_atomic, and a server notification publish that goes through TaskRun so it gets presence gating and the ACP envelope. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
An unstamped stream at the length cap may be a trimmed tail, so the history read still loads the run's log for it. A persisted server notification sits in both stores without an event id, and the merge now keeps one copy. Task.mutate_state_atomic hands the mutator a deep copy, so an in-place edit of a nested value is saved. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… the log Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… log append Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
parse_exec_command, normalize_tool_name and INFO_SYNTHETIC_PREFIX move to products/posthog_ai/backend/exec_commands.py so production code can read single-exec tool calls without importing eval tooling. The eval harness and scorers import them from the new module. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…d the facade can_create_scout holds the create endpoint's skill editor check, and the new scout_creation_available facade read uses it after the enrollment check, so a caller can decide whether to offer scout creation with the same rule the endpoint enforces. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
scout_creation_available takes a team id and a user id and resolves the models inside Signals, so no ORM instance crosses the facade. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…ing scouts Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…offering scouts Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
build_system_one_client returns a client for the Go ai-gateway's /v1/systemone route where AI_GATEWAY_URL is set, and for TypeSafe elsewhere. Callers name one model per server, because the two serve different models. The System One request and answer types move out of the TypeSafe egress domain into posthog/llm/system_one.py, so both servers share them. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
… up front Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Classify each completed PostHog AI turn and publish an end-of-turn suggestion frame. The task runs on a new posthog_ai Celery queue, a short Redis lock drops duplicate turn reports before they are queued, and the offer ledger in the task state is the only stored outcome of a card: a reload reads it from the new state endpoint. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…blishing - read insight and issue refs from the MCP app-data envelope, and resolve tool identity from the adapter's _meta.posthog stamp - note a follow-up that is still running instead of claiming its turn - skip conversations past the resume chain depth, where turn indexes slide - count a card only when it reached the run log - cap the insight and issue options Jev sees - release the enqueue dedup key when queueing fails, and keep it only for the settle wait - send the Luna draft cap as max_completion_tokens Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…g's cost The Luna drafter now uses build_openai_client, so it reaches the Go ai-gateway where AI_GATEWAY_URL is set and bills the wallet of the team that owns that key. The Python fallback moves from the posthog_ai route, which bills the customer's AI credits, to the unbilled growth route. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
The judge now asks posthog/hogference/jevk5-fp8-0.2 through the ai-gateway where it is configured, and jev-1.13.0 through TypeSafe elsewhere. Ref options cap at 15, so a choice fits the gateway model's 16 options. The classified event records the model the configured server was asked for. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
742e8b8 to
ec3c45b
Compare
|
Merging to
After your PR is submitted to the merge queue, this comment will be automatically updated with its status. If the PR fails, failure details will also be posted here |
Problem
Note
This layer needs a worker consuming the new
posthog_aiCelery queue. https://github.com/PostHog/charts/pull/16307 adds it and merged on 2026-09-25. Confirm the worker runs in each region before this layer merges.Changes
posthog/hogference/jevk5-fp8-0.2where it is configured, through the client from feat(llm): add a system one client that prefers the ai-gateway #106735. The judge opts into the TypeSafe fallback, so TypeSafe answers withjev-1.13.0elsewhere.jev-1.13.0. An earlier benchmark run of the gateway model at the same threshold is in chore(growth): turn suggestion judge benchmark #106433.Task.stateis the only stored outcome of a card. The newGET turn_suggestions/state/endpoint serves it to a reloaded thread.POST turn_suggestions/resolve/records a dismiss or an accept. It tells other open tabs through a frame on the offer's run and on the task's latest run.posthog-ai-turn-suggestionsflag, AI data processing approval, a conversation started in the web app, and a configured System One server (AI_GATEWAY_URLwithAI_GATEWAY_API_KEY, orTYPESAFE_API_KEY).posthog_aiCelery queue, two seconds after the turn, so the last frames of the answer have landed.SET NXdrops the duplicates before they reach the queue.flowchart LR A[Turn completes] --> D[Redis dedup] D --> E[posthog_ai queue] E --> L[Offer ledger gates] L --> G{{Jev judges the masked turn}} G -->|scout or notebook| R{{Luna drafts the text}} G -->|alert, subscription, error alert| H[Suggestion frame] R --> H classDef phBlue fill:#1d4aff,stroke:#1d4aff,color:#fff; classDef phYellow fill:#f9bd2b,stroke:#f9bd2b,color:#000; classDef phGray fill:#e5e7eb,stroke:#c7ccd1,color:#000; class A phYellow; class G,R phBlue; class D,E,L,H phGray;How did you test this code?
test_turn_suggestions.pycovers the transcript fold, the masked Jev request, the offer policy, the ledger rules and races, and the gates.session/promptframes merge every earlier answer into the last turn.test_turn_suggestions_api.pycovers the state and resolve endpoints.Release status
Automatic notifications
Docs update
None: the feature is behind a flag that is off by default.
🤖 Agent context
Autonomy: Human-driven (agent-assisted)
Agent: Claude Code, Claude Fable 5.1 wrote the original #101991. Claude Opus 5.5 switched the judge to Jev, split the stack and fixed the review findings.
growthroute, never the customer's AI credits.TYPESAFE_API_KEYproduction wiring landed in https://github.com/PostHog/charts/pull/16183.🤖 Generated with Claude Code