Skip to content

refactor(batch-exports): decouple kea logics by extracting batchExportConfigLogic#52003

Open
rossgray wants to merge 12 commits intomasterfrom
ross/batch-exports-kea-refactor
Open

refactor(batch-exports): decouple kea logics by extracting batchExportConfigLogic#52003
rossgray wants to merge 12 commits intomasterfrom
ross/batch-exports-kea-refactor

Conversation

@rossgray
Copy link
Contributor

@rossgray rossgray commented Mar 23, 2026

Problem

The batch export child logics (batchExportRunsLogic, batchExportBackfillsLogic, batchExportBackfillModalLogic) were tightly coupled to batchExportConfigurationLogic via connect() — a heavyweight logic that owns form state, validation, beforeUnload, and test step execution. This meant child logics couldn't be mounted independently.

When HogFunctionBackfills tried to render the backfills tab for hog function destinations, it needed to mount batchExportBackfillsLogic, which auto-mounted the full form logic — causing a Kea store path error because no parent BindLogic provided it.

Additionally, hogFunctionBackfillsLogic had a race condition: its afterMount checked configuration.batch_export_id before the config had loaded, causing an unnecessary call to enable_backfills on every tab open.

Changes

Most of the changes here are either adding/updating tests or due to renaming the logic.

Therefore when reviewing these changes, it probably makes most sense to review products/batch_exports/frontend/README.md first, as it provides the architectural overview.

Extract batchExportConfigLogic (~35 lines) — a lightweight logic that only loads and caches batchExportConfig from the API. Child logics now connect here instead of the form logic, so they can be mounted independently (e.g. from the hog function backfills tab).

Rename batchExportConfigurationLogicbatchExportConfigFormLogic — clarifies that this logic owns the editing experience (form state, validation, dirty-checking, test steps, save/delete). It now connects to batchExportConfigLogic for its config data.

Fix race condition in hogFunctionBackfillsLogic — replaced afterMount with a loadHogFunctionSuccess listener so enableHogFunctionBackfills only fires after the config has loaded and batch_export_id is confirmed missing.

Add architecture docs to products/batch_exports/frontend/README.md with a logic dependency tree, component table, and external consumer notes.

How did you test this code?

  • All existing unit tests pass (pnpm --filter=@posthog/frontend jest batchExport hogFunctionBackfills --no-coverage)
  • Playwright E2E tests pass
  • New tests were added for hog function backfills (unit tests for Kea logic, E2E test and Storybook story (visual regression))
  • Ran stack locally and tested all the tabs in both batch exports scene and hog function backfills

Publish to changelog?

No

Docs update

No docs update needed.

🤖 LLM context

This PR was co-authored with Claude Code. The refactor was planned using a dependency graph analysis of the batch export Kea logics, with alternatives (passing config as props, wrapping with BindLogic for the full form logic) considered and rejected. See products/batch_exports/frontend/README.md for the architecture overview.

@greptile-apps
Copy link
Contributor

greptile-apps bot commented Mar 23, 2026

Prompt To Fix All With AI
This is a comment left during a code review.
Path: frontend/src/scenes/hog-functions/backfills/hogFunctionBackfillsLogic.tsx
Line: 63-69

Comment:
**Race condition fix may itself miss the event**

The `loadHogFunctionSuccess` listener only catches the action if it fires *after* `hogFunctionBackfillsLogic` is mounted. However, `LemonTabs` renders only the active tab's content (see `LemonTabs.tsx` line 152-154: `{activeTab.content}`), meaning `HogFunctionBackfills` — and therefore this logic — is not mounted until the user clicks the Backfills tab.

In the common user journey:
1. User navigates to the hog function page → `hogFunctionConfigurationLogic` loads → `loadHogFunctionSuccess` fires.
2. User clicks the Backfills tab → `hogFunctionBackfillsLogic` mounts.
3. At this point `loadHogFunctionSuccess` has already fired and the listener never triggers.

For a hog function without `batch_export_id`, the skeleton is displayed indefinitely because `enableHogFunctionBackfills` is never called.

A robust solution handles both cases: config already loaded at mount time, and config loading after mount:

```ts
afterMount(({ actions, values }) => {
    // If hogFunctionConfigurationLogic has already loaded (configuration.id is set)
    // and batch_export_id is genuinely absent, enable immediately.
    if (values.configuration?.id && !values.configuration.batch_export_id) {
        actions.enableHogFunctionBackfills()
    }
}),
listeners(({ actions, values }) => ({
    loadHogFunctionSuccess: () => {
        // Handle the case where config loads after this logic mounts.
        if (!values.configuration.batch_export_id) {
            actions.enableHogFunctionBackfills()
        }
    },
})),
```

Note: the E2E test (`hog-function-backfills.spec.ts`) and the Storybook mock both set `batch_export_id` on the hog function, so they exercise only the already-enabled path and would not catch this regression.

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: frontend/src/scenes/data-pipelines/batch-exports/batchExportBackfillsLogic.test.ts
Line: 104-106

Comment:
**Superfluous dependency in test setup**

`batchExportBackfillsLogic` now connects to `batchExportConfigLogic` (the lightweight loader), not `batchExportConfigFormLogic`. Pre-mounting the heavyweight form logic here works only because `batchExportConfigFormLogic` in turn auto-mounts `batchExportConfigLogic` via its own `connect()`. The form logic itself is not needed by the subject under test.

This is misleading — it implies the backfills logic still depends on the form logic — and it mounts a large chunk of logic (form state, validation, `beforeUnload`, etc.) that this test doesn't exercise. Replacing with the lightweight logic better reflects the actual production dependency:

```suggestion
        const configLogic = batchExportConfigLogic({ id: MOCK_BATCH_EXPORT_ID })
        configLogic.mount()
        await expectLogic(configLogic).toFinishAllListeners()
```

You'll need to add `import { batchExportConfigLogic } from './batchExportConfigLogic'` at the top of the file.

How can I resolve this? If you propose a fix, please make it concise.

---

This is a comment left during a code review.
Path: frontend/src/scenes/hog-functions/backfills/hogFunctionBackfillsLogic.test.ts
Line: 26-63

Comment:
**Test file doesn't test `hogFunctionBackfillsLogic`**

The `describe` block is named `'hogFunctionBackfillsLogic'` but the test mounts and asserts on `batchExportBackfillsLogic`, never touching `hogFunctionBackfillsLogic` itself.

The critical behavioral change in this PR — the `loadHogFunctionSuccess` listener calling `enableHogFunctionBackfills` — has no test coverage. Consider adding tests that:
- Verify `enableHogFunctionBackfills` is called when `loadHogFunctionSuccess` fires and `batch_export_id` is absent.
- Verify `enableHogFunctionBackfills` is NOT called when `loadHogFunctionSuccess` fires and `batch_export_id` is already set.
- Verify the logic handles the "already loaded at mount" scenario (the P1 concern above).

Having test coverage here would also catch the missed-event edge case described in the comment on `hogFunctionBackfillsLogic.tsx`.

How can I resolve this? If you propose a fix, please make it concise.

Reviews (1): Last reviewed commit: "test: fix mock" | Re-trigger Greptile

@rossgray rossgray force-pushed the ross/batch-exports-kea-refactor branch from d9513db to 25e4537 Compare March 23, 2026 20:46
@github-actions
Copy link
Contributor

github-actions bot commented Mar 23, 2026

Size Change: 0 B

Total Size: 110 MB

ℹ️ View Unchanged
Filename Size
frontend/dist/368Hedgehogs 5.27 kB
frontend/dist/abap 14.2 kB
frontend/dist/Action 20.5 kB
frontend/dist/Actions 1.03 kB
frontend/dist/AdvancedActivityLogsScene 34 kB
frontend/dist/AgenticAuthorize 5.27 kB
frontend/dist/apex 3.96 kB
frontend/dist/ApprovalDetail 16.2 kB
frontend/dist/array.full.es5.js 324 kB
frontend/dist/array.full.js 420 kB
frontend/dist/array.js 176 kB
frontend/dist/AsyncMigrations 13.2 kB
frontend/dist/AuthorizationStatus 717 B
frontend/dist/azcli 852 B
frontend/dist/bat 1.85 kB
frontend/dist/BatchExportScene 70.5 kB
frontend/dist/bicep 2.56 kB
frontend/dist/Billing 493 B
frontend/dist/BillingSection 20.7 kB
frontend/dist/BoxPlot 5.01 kB
frontend/dist/browserAll-0QZMN1W2 37.4 kB
frontend/dist/ButtonPrimitives 562 B
frontend/dist/CalendarHeatMap 4.82 kB
frontend/dist/cameligo 2.2 kB
frontend/dist/changeRequestsLogic 544 B
frontend/dist/CLIAuthorize 11.3 kB
frontend/dist/CLILive 3.99 kB
frontend/dist/clojure 9.65 kB
frontend/dist/coffee 3.6 kB
frontend/dist/Cohort 23.3 kB
frontend/dist/CohortCalculationHistory 6.24 kB
frontend/dist/Cohorts 9.41 kB
frontend/dist/ConfirmOrganization 4.5 kB
frontend/dist/conversations.js 63.8 kB
frontend/dist/Coupons 731 B
frontend/dist/cpp 5.31 kB
frontend/dist/Create 666 B
frontend/dist/crisp-chat-integration.js 1.88 kB
frontend/dist/csharp 4.53 kB
frontend/dist/csp 1.43 kB
frontend/dist/css 4.51 kB
frontend/dist/cssMode 4.18 kB
frontend/dist/CustomCssScene 3.56 kB
frontend/dist/CustomerAnalyticsConfigurationScene 2 kB
frontend/dist/CustomerAnalyticsScene 26.4 kB
frontend/dist/CustomerJourneyBuilderScene 1.61 kB
frontend/dist/CustomerJourneyTemplatesScene 7.42 kB
frontend/dist/customizations.full.js 17.8 kB
frontend/dist/CyclotronJobInputAssignee 1.33 kB
frontend/dist/CyclotronJobInputTicketTags 717 B
frontend/dist/cypher 3.4 kB
frontend/dist/dart 4.26 kB
frontend/dist/Dashboard 1 kB
frontend/dist/Dashboards 14.7 kB
frontend/dist/DataManagementScene 646 B
frontend/dist/DataPipelinesNewScene 2.33 kB
frontend/dist/DataWarehouseScene 1.11 kB
frontend/dist/DataWarehouseSourceScene 634 B
frontend/dist/Deactivated 1.14 kB
frontend/dist/dead-clicks-autocapture.js 12.5 kB
frontend/dist/DeadLetterQueue 5.41 kB
frontend/dist/DebugScene 20.1 kB
frontend/dist/decompressionWorker 2.85 kB
frontend/dist/decompressionWorker.js 2.85 kB
frontend/dist/DefinitionEdit 7.13 kB
frontend/dist/DefinitionView 22.8 kB
frontend/dist/DestinationsScene 2.72 kB
frontend/dist/dist 575 B
frontend/dist/dockerfile 1.88 kB
frontend/dist/EarlyAccessFeature 685 B
frontend/dist/EarlyAccessFeatures 2.85 kB
frontend/dist/ecl 5.35 kB
frontend/dist/EditorScene 839 B
frontend/dist/elixir 10.3 kB
frontend/dist/EmailMFAVerify 2.99 kB
frontend/dist/EndpointScene 37.2 kB
frontend/dist/EndpointsScene 21 kB
frontend/dist/ErrorTrackingConfigurationScene 2.2 kB
frontend/dist/ErrorTrackingIssueFingerprintsScene 6.99 kB
frontend/dist/ErrorTrackingIssueScene 82.1 kB
frontend/dist/ErrorTrackingScene 13 kB
frontend/dist/EvaluationTemplates 575 B
frontend/dist/EventsScene 2.45 kB
frontend/dist/exception-autocapture.js 11.7 kB
frontend/dist/Experiment 261 kB
frontend/dist/Experiments 17.1 kB
frontend/dist/exporter 21 MB
frontend/dist/exporter.js 21 MB
frontend/dist/ExportsScene 3.88 kB
frontend/dist/FeatureFlag 102 kB
frontend/dist/FeatureFlags 572 B
frontend/dist/FeatureFlagTemplatesScene 7.05 kB
frontend/dist/FlappyHog 5.8 kB
frontend/dist/flow9 1.81 kB
frontend/dist/freemarker2 16.7 kB
frontend/dist/fsharp 2.99 kB
frontend/dist/go 2.66 kB
frontend/dist/graphql 2.27 kB
frontend/dist/Group 14.5 kB
frontend/dist/Groups 3.94 kB
frontend/dist/GroupsNew 7.36 kB
frontend/dist/handlebars 7.37 kB
frontend/dist/hcl 3.6 kB
frontend/dist/HealthScene 12 kB
frontend/dist/HeatmapNewScene 4.18 kB
frontend/dist/HeatmapRecordingScene 3.94 kB
frontend/dist/HeatmapScene 6.05 kB
frontend/dist/HeatmapsScene 3.89 kB
frontend/dist/HogFunctionScene 59.1 kB
frontend/dist/HogRepl 7.38 kB
frontend/dist/html 5.6 kB
frontend/dist/htmlMode 4.64 kB
frontend/dist/image-blob-reduce.esm 49.4 kB
frontend/dist/InboxScene 54.6 kB
frontend/dist/index 255 kB
frontend/dist/index.js 255 kB
frontend/dist/ini 1.11 kB
frontend/dist/InsightOptions 4.81 kB
frontend/dist/InsightScene 27.1 kB
frontend/dist/IntegrationsRedirect 744 B
frontend/dist/intercom-integration.js 1.93 kB
frontend/dist/InviteSignup 13.3 kB
frontend/dist/java 3.23 kB
frontend/dist/javascript 996 B
frontend/dist/jsonMode 13.9 kB
frontend/dist/julia 7.24 kB
frontend/dist/kotlin 3.41 kB
frontend/dist/lazy 151 kB
frontend/dist/LegacyPluginScene 21.2 kB
frontend/dist/less 3.9 kB
frontend/dist/lexon 2.45 kB
frontend/dist/lib 2.23 kB
frontend/dist/Link 468 B
frontend/dist/LinkScene 24.9 kB
frontend/dist/LinksScene 4.21 kB
frontend/dist/liquid 4.54 kB
frontend/dist/LiveDebugger 19.2 kB
frontend/dist/LiveEventsTable 4.46 kB
frontend/dist/LLMAnalyticsClusterScene 15.7 kB
frontend/dist/LLMAnalyticsClustersScene 43.1 kB
frontend/dist/LLMAnalyticsDatasetScene 19.7 kB
frontend/dist/LLMAnalyticsDatasetsScene 3.29 kB
frontend/dist/LLMAnalyticsEvaluation 40.5 kB
frontend/dist/LLMAnalyticsEvaluationsScene 29.3 kB
frontend/dist/LLMAnalyticsPlaygroundScene 36.4 kB
frontend/dist/LLMAnalyticsScene 113 kB
frontend/dist/LLMAnalyticsSessionScene 13.4 kB
frontend/dist/LLMAnalyticsTraceScene 127 kB
frontend/dist/LLMAnalyticsUsers 526 B
frontend/dist/LLMASessionFeedbackDisplay 4.85 kB
frontend/dist/LLMPromptScene 16.9 kB
frontend/dist/LLMPromptsScene 4.23 kB
frontend/dist/Login 8.51 kB
frontend/dist/Login2FA 4.22 kB
frontend/dist/logs.js 38.3 kB
frontend/dist/LogsScene 18.5 kB
frontend/dist/lua 2.13 kB
frontend/dist/m3 2.82 kB
frontend/dist/ManagedMigration 14 kB
frontend/dist/markdown 3.79 kB
frontend/dist/MarketingAnalyticsScene 24.7 kB
frontend/dist/MaterializedColumns 10.2 kB
frontend/dist/Max 767 B
frontend/dist/mdx 5.38 kB
frontend/dist/MessageTemplate 16.3 kB
frontend/dist/MetricsScene 844 B
frontend/dist/mips 2.59 kB
frontend/dist/ModelsScene 13.7 kB
frontend/dist/MonacoDiffEditor 403 B
frontend/dist/MoveToPostHogCloud 4.46 kB
frontend/dist/msdax 4.92 kB
frontend/dist/mysql 11.3 kB
frontend/dist/NavTabChat 4.55 kB
frontend/dist/NewSourceWizard 758 B
frontend/dist/NewTabScene 647 B
frontend/dist/NodeDetailScene 15.5 kB
frontend/dist/NotebookCanvasScene 3.04 kB
frontend/dist/NotebookPanel 5.07 kB
frontend/dist/NotebookScene 8.06 kB
frontend/dist/NotebooksScene 7.61 kB
frontend/dist/OAuthAuthorize 573 B
frontend/dist/objective-c 2.42 kB
frontend/dist/Onboarding 672 kB
frontend/dist/OnboardingCouponRedemption 1.2 kB
frontend/dist/pascal 3 kB
frontend/dist/pascaligo 2.01 kB
frontend/dist/passkeyLogic 484 B
frontend/dist/PasswordReset 4.33 kB
frontend/dist/PasswordResetComplete 2.95 kB
frontend/dist/perl 8.26 kB
frontend/dist/PersonScene 16 kB
frontend/dist/PersonsScene 4.75 kB
frontend/dist/pgsql 13.5 kB
frontend/dist/php 8.03 kB
frontend/dist/PipelineStatusScene 6.24 kB
frontend/dist/pla 1.69 kB
frontend/dist/posthog 251 kB
frontend/dist/postiats 7.86 kB
frontend/dist/powerquery 17 kB
frontend/dist/powershell 3.28 kB
frontend/dist/PreflightCheck 5.54 kB
frontend/dist/product-tours.js 115 kB
frontend/dist/ProductTour 274 kB
frontend/dist/ProductTours 4.72 kB
frontend/dist/ProjectHomepage 21.9 kB
frontend/dist/protobuf 9.05 kB
frontend/dist/pug 4.83 kB
frontend/dist/python 4.79 kB
frontend/dist/qsharp 3.2 kB
frontend/dist/r 3.14 kB
frontend/dist/razor 9.36 kB
frontend/dist/recorder-v2.js 111 kB
frontend/dist/recorder.js 111 kB
frontend/dist/redis 3.56 kB
frontend/dist/redshift 11.8 kB
frontend/dist/RegionMap 29.5 kB
frontend/dist/render-query 20.8 MB
frontend/dist/render-query.js 20.8 MB
frontend/dist/ResourceTransfer 9.16 kB
frontend/dist/restructuredtext 3.91 kB
frontend/dist/RevenueAnalyticsScene 25.6 kB
frontend/dist/ruby 8.51 kB
frontend/dist/rust 4.17 kB
frontend/dist/SavedInsights 664 B
frontend/dist/sb 1.83 kB
frontend/dist/scala 7.33 kB
frontend/dist/scheme 1.77 kB
frontend/dist/scss 6.41 kB
frontend/dist/SdkDoctorScene 11 kB
frontend/dist/SessionAttributionExplorerScene 6.62 kB
frontend/dist/SessionGroupSummariesTable 4.64 kB
frontend/dist/SessionGroupSummaryScene 17.1 kB
frontend/dist/SessionProfileScene 15.9 kB
frontend/dist/SessionRecordingDetail 1.74 kB
frontend/dist/SessionRecordingFilePlaybackScene 4.48 kB
frontend/dist/SessionRecordings 742 B
frontend/dist/SessionRecordingsKiosk 8.87 kB
frontend/dist/SessionRecordingsPlaylistScene 4.08 kB
frontend/dist/SessionRecordingsSettingsScene 1.91 kB
frontend/dist/SessionsScene 3.88 kB
frontend/dist/SettingsScene 3 kB
frontend/dist/SharedMetric 15.7 kB
frontend/dist/SharedMetrics 515 B
frontend/dist/shell 3.08 kB
frontend/dist/SignupContainer 22.8 kB
frontend/dist/Site 1.2 kB
frontend/dist/solidity 18.6 kB
frontend/dist/sophia 2.77 kB
frontend/dist/SourcesScene 5.98 kB
frontend/dist/sourceWizardLogic 662 B
frontend/dist/sparql 2.56 kB
frontend/dist/sql 10.3 kB
frontend/dist/SqlVariableEditScene 7.26 kB
frontend/dist/st 7.41 kB
frontend/dist/StartupProgram 21.2 kB
frontend/dist/SupportSettingsScene 36.2 kB
frontend/dist/SupportTicketScene 22.8 kB
frontend/dist/SupportTicketsScene 733 B
frontend/dist/Survey 746 B
frontend/dist/SurveyFormBuilder 1.55 kB
frontend/dist/Surveys 18.2 kB
frontend/dist/surveys.js 89.8 kB
frontend/dist/SurveyWizard 56.2 kB
frontend/dist/swift 5.28 kB
frontend/dist/SystemStatus 16.9 kB
frontend/dist/systemverilog 7.62 kB
frontend/dist/TaskDetailScene 19.1 kB
frontend/dist/TaskTracker 16.4 kB
frontend/dist/tcl 3.57 kB
frontend/dist/toolbar 9.84 MB
frontend/dist/toolbar.js 9.84 MB
frontend/dist/ToolbarLaunch 2.53 kB
frontend/dist/tracing-headers.js 1.74 kB
frontend/dist/TracingScene 14.5 kB
frontend/dist/TransformationsScene 1.97 kB
frontend/dist/tsMode 24 kB
frontend/dist/twig 5.98 kB
frontend/dist/TwoFactorReset 4 kB
frontend/dist/typescript 240 B
frontend/dist/typespec 2.83 kB
frontend/dist/Unsubscribe 1.63 kB
frontend/dist/UserInterview 4.55 kB
frontend/dist/UserInterviews 2.03 kB
frontend/dist/vb 5.8 kB
frontend/dist/VercelConnect 4.03 kB
frontend/dist/VercelLinkError 1.92 kB
frontend/dist/VerifyEmail 4.49 kB
frontend/dist/vimMode 211 kB
frontend/dist/VisualReviewRunScene 18.7 kB
frontend/dist/VisualReviewRunsScene 6.14 kB
frontend/dist/VisualReviewSettingsScene 9.97 kB
frontend/dist/web-vitals.js 6.39 kB
frontend/dist/WebAnalyticsScene 5.72 kB
frontend/dist/WebGLRenderer-DYjOwNoG 60.3 kB
frontend/dist/WebGPURenderer-B_wkl_Ja 36.3 kB
frontend/dist/WebScriptsScene 2.58 kB
frontend/dist/webworkerAll-puPV1rBA 330 B
frontend/dist/wgsl 7.35 kB
frontend/dist/Wizard 4.47 kB
frontend/dist/WorkflowScene 101 kB
frontend/dist/WorkflowsScene 47.5 kB
frontend/dist/WorldMap 4.75 kB
frontend/dist/xml 2.98 kB
frontend/dist/yaml 4.61 kB

compressed-size-action

@rossgray rossgray added the update-snapshots Enable auto commit snapshots for Storybook and Playwright label Mar 24, 2026
rossgray added 10 commits March 24, 2026 11:11
Split the heavyweight batchExportConfigFormLogic into two: a lightweight
batchExportConfigLogic that only loads/caches the config, and the existing
form logic for editing. Child logics (runs, backfills, modal) now connect
to the config logic instead, allowing them to mount independently — fixing
the Kea mounting error when rendering backfills from the hog function scene.
@rossgray rossgray force-pushed the ross/batch-exports-kea-refactor branch from 25e4537 to 5bcccd7 Compare March 24, 2026 11:12
@rossgray rossgray requested review from a team and tomasfarias March 24, 2026 12:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

update-snapshots Enable auto commit snapshots for Storybook and Playwright

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant