Skip to content

feat(data-warehouse): implement the kapa.ai import source - #108635

Open
Gilbert09 wants to merge 1 commit into
masterfrom
tom/dwh-source-kapa_ai
Open

Gilbert09 wants to merge 1 commit into
masterfrom
tom/dwh-source-kapa_ai

Conversation

@Gilbert09

Copy link
Copy Markdown
Member

Problem

PostHog users can now import their kapa.ai data into the data warehouse.

Changes

  • Threads use full refresh to capture conversation edits without relying on unverified update filters.
  • End users use full refresh to capture profile changes.
  • Sources use full refresh to capture knowledge source changes.
  • Source groups use full refresh to capture grouping changes.
  • Integrations use full refresh to capture configuration changes.
  • Activity uses full refresh to replace the project's aggregate snapshot.
  • Top question periods use full refresh to discover available analytics periods.
  • Coverage gap periods use full refresh to discover available analytics periods.
  • Top questions use full refresh to reload clusters for each available period.
  • Coverage gaps use full refresh to reload clusters for each available period.
  • Authentication uses a project API key in the X-API-KEY header.
  • Paginated imports resume from saved checkpoints through the shared REST client.
  • Question-answer records remain nested in threads.
  • Incremental sync remains disabled until updated_since filtering is verified against a live account.
  • The source launches in alpha.

How did you test this code?

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

Ran the source and shared source registry suites with python -m pytest -q -p no:cacheprovider.

  • Pagination tests catch skipped pages, lost nested data, premature checkpoints, and restarted completed imports.
  • Period tests catch missing child pages, incorrect period metadata, and cluster key collisions across periods.
  • Response tests catch malformed collections that could erase tables and incorrect unpaginated response shapes.
  • Authentication tests catch invalid requests, incorrect permission errors, leaked keys through pagination, and missing retries for transient failures.
  • Pipeline tests catch retried terminal authentication failures, unknown tables, and stale watermarks filtering full refreshes.
  • Registry tests catch incompatible categories, versions, schema capabilities, and generated configurations.

Nothing was run against a live kapa.ai account. These documentation-based request shapes remain unverified against a live account:

  • Project-scoped query and ingestion paths with X-API-KEY authentication.
  • Thread include, sort=asc, page_size, and cursor parameters.
  • End-user, source, and source-group pagination through response next URLs.
  • Period and cluster cursor pagination, including period-specific cluster paths.
  • Unpaginated integration lists and activity objects.

Release status

  • No feature flag controls this change
  • This change is behind a feature flag and is not available to users
  • This change makes a previously flagged feature available to everyone

Automatic notifications

  • Publish to changelog?

Docs update

The posthog.com source page is a follow-up.

Sync conversations, end users, knowledge sources, integrations, and activity.
Import completed analytics periods and their question and coverage-gap clusters.
Use project API keys and resumable full refresh through the shared REST client.
Copilot AI balanced review requested due to automatic review settings September 29, 2026 18:40
@Gilbert09 Gilbert09 self-assigned this Sep 29, 2026
@Gilbert09 Gilbert09 added the stamphog Request AI approval (no full review) label Sep 29, 2026 — with Talyn App

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@trunk-io

trunk-io Bot commented Sep 29, 2026

Copy link
Copy Markdown

Merging to master in this repository is managed by Trunk.

  • To merge this pull request, check the box to the left or comment /trunk merge below.

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

@github-actions

Copy link
Copy Markdown
Contributor

Hey @Gilbert09! 👋

It looks like your git author email on this PR isn't your @posthog.com address (owerstom@gmail.com). Since you're on the PostHog team, it's worth pointing your local git author email at your @posthog.com address. Why it matters:

  • Consistent work identity in git history — internal tooling that attributes commits to team members keys off your @posthog.com address.
  • Keeps team contributions easy to tell apart from external community ones when scanning history.

You can fix it for this repo with:

git config user.email "you@posthog.com"

Or set it globally with git config --global user.email "you@posthog.com". No need to redo this PR — just a nudge for next time. 🙂

@github-actions

github-actions Bot commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

🦔 Hogbox preview · ✅ ready

▶ 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 0edc35c · box box-0b8ccf04bb37 · ready in 771s (push → usable) · build log · rebuilds on every push, torn down on close

@pr-assigner-resolver-posthog
pr-assigner-resolver-posthog Bot requested a review from a team September 29, 2026 18:40

@stamphog stamphog Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved.

This is an additive, alpha-flagged warehouse import source written by an owning-team author with strong familiarity, and it has thorough tests. It follows the established source pattern: the API key is sent only to the fixed kapa.ai host, redirects are off, and the project ID is checked as a UUID before it goes into a URL.

  • Author wrote 88% of the modified lines and has 116 merged PRs in these paths (familiarity STRONG).
  • copilot-pull-request-reviewer[bot] reviewed the current head.
Gate mechanics and policy version
Gate Result
prerequisites ✓ all clear
deny-list ✓ no deny categories matched
size ✓ 441L, 5F substantive, 1 binary; 778L/10F incl. docs/generated/snapshots — within ceiling
tier ✓ T1-agent / T1d-complex (778L, 10F, two-areas, feat)
stamphog 2.3.1 .stamphog/policy.yml @ 0edc35c · reviewed head 0edc35c

@github-actions

github-actions Bot commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

🤖 CI report

⚠️ Trunk lane — backend Python lane

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

⚠️ Duplication (Python) — 2 new duplicated blocks (worst 159 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
products/warehouse_sources/backend/temporal/data_imports/sources/companycam/source.py:1 products/warehouse_sources/backend/temporal/data_imports/sources/kapa_ai/source.py:1 21 159
products/warehouse_sources/backend/temporal/data_imports/sources/kapa_ai/source.py:1 products/warehouse_sources/backend/temporal/data_imports/sources/papersign/source.py:1 21 157
✅ 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.

✅ Bundle size — no change

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

Total: 68.97 MiB · no change

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
1.58 MiB · 22 files no change █████████░ 85.8% 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.52 MiB · 629 files no change █████████░ 87.4% of 4.03 MiB
authenticated shell (every logged-in page)
src/scenes/AuthenticatedShell.tsx
7.35 MiB · 2,339 files no change █████████░ 88.1% 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.14_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
216.9 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.5 KiB src/lib/api.ts
88.5 KiB src/products.tsx
69.4 KiB src/lib/lemon-ui/icons/icons.tsx
40.1 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.4 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.14_@types+react@18.3.27_react@18.3.1/node_modules/posthog-js/dist/module.mjs
272.3 KiB src/taxonomy/core-filter-definitions-by-group.json
216.9 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.5 KiB src/lib/api.ts
98.8 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
88.5 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.16 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.16 MiB · 19 files no change ████░░░░░░ 37.8% 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
805.5 KiB dist/toolbar/toolbar-app-ZATDHZQQ.css
651.7 KiB dist/toolbar/chunk-chunk-7ZORLPSY.js
259.4 KiB dist/toolbar/chunk-chunk-2FGAEFTI.js
138.3 KiB dist/toolbar/chunk-chunk-UA3V2PCC.js
131.8 KiB dist/toolbar/chunk-chunk-FDH2IBXT.js
75.2 KiB dist/toolbar/toolbar-app-SD4VQXWA.js
69.0 KiB dist/toolbar/chunk-chunk-TSAL54PB.js
35.6 KiB dist/toolbar/chunk-chunk-63EWNJRZ.js
21.0 KiB dist/toolbar/chunk-chunk-F5GDSM5D.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 — 🔺 +57.8 KiB (+0.0%)

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

Total: 947.85 MiB · 🔺 +57.8 KiB (+0.0%)

✅ Playwright — all passed

All tests passed.

View test results →

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

🧪 Backend test coverage

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

File Patch Uncovered changed lines
products/warehouse_sources/backend/temporal/data_imports/sources/kapa_ai/source.py 91.7% 71, 74
products/warehouse_sources/backend/temporal/data_imports/sources/kapa_ai/kapa_ai.py 98.7% 120

🤖 Agents: add a test covering the lines above, or note why under "How did you test this code?". Machine-readable gap list: the patch-coverage artifact on this run (gh run download 242765029253128 -n patch-coverage), or the coverage-data block at the end of this comment.

Per-product line coverage (touched products)
Product Coverage Lines
demo ███████████░░░░░░░░░ 52.8% 1,411 / 2,673
batch_exports ████████████████░░░░ 81.2% 21,526 / 26,502
cdp ██████████████████░░ 88.2% 4,545 / 5,155
mcp_analytics ██████████████████░░ 89.2% 5,038 / 5,651
product_tours ██████████████████░░ 89.3% 1,331 / 1,491
dashboards ██████████████████░░ 89.5% 6,904 / 7,714
data_warehouse ██████████████████░░ 90.0% 14,027 / 15,589
notebooks ██████████████████░░ 90.2% 15,297 / 16,964
signals ██████████████████░░ 90.2% 57,838 / 64,097
cohorts ██████████████████░░ 90.4% 8,420 / 9,316
streamlit_apps ██████████████████░░ 90.8% 2,684 / 2,956
managed_warehouse ██████████████████░░ 91.0% 10,252 / 11,263
tasks ██████████████████░░ 91.1% 75,525 / 82,874
data_modeling ██████████████████░░ 91.4% 10,554 / 11,543
exports ██████████████████░░ 91.6% 9,680 / 10,562
engineering_analytics ██████████████████░░ 91.7% 11,032 / 12,030
business_knowledge ██████████████████░░ 92.2% 7,684 / 8,330
conversations ███████████████████░ 92.5% 28,734 / 31,062
early_access_features ███████████████████░ 92.6% 1,332 / 1,439
stamphog ███████████████████░ 92.8% 8,109 / 8,742
canvas ███████████████████░ 92.8% 6,873 / 7,405
approvals ███████████████████░ 93.0% 3,974 / 4,271
mcp_registry ███████████████████░ 93.1% 1,670 / 1,794
notifications ███████████████████░ 93.2% 1,145 / 1,229
error_tracking ███████████████████░ 93.2% 16,359 / 17,547
surveys ███████████████████░ 93.3% 6,571 / 7,040
slack_app ███████████████████░ 93.4% 13,995 / 14,989
autoresearch ███████████████████░ 93.6% 8,481 / 9,061
context_layer ███████████████████░ 93.9% 3,415 / 3,638
web_analytics ███████████████████░ 94.0% 21,815 / 23,218
alerts ███████████████████░ 94.0% 8,570 / 9,114
billing_alerts ███████████████████░ 94.1% 2,094 / 2,226
mcp_store ███████████████████░ 94.4% 8,940 / 9,472
ai_observability ███████████████████░ 94.5% 24,473 / 25,896
workflows ███████████████████░ 94.6% 15,222 / 16,093
wizard ███████████████████░ 94.7% 6,150 / 6,496
reminders ███████████████████░ 94.8% 760 / 802
review_hog ███████████████████░ 95.0% 11,507 / 12,119
endpoints ███████████████████░ 95.1% 9,206 / 9,681
annotations ███████████████████░ 95.1% 817 / 859
customer_analytics ███████████████████░ 95.2% 25,636 / 26,938
marketing_analytics ███████████████████░ 95.3% 19,566 / 20,528
posthog_ai ███████████████████░ 95.3% 2,488 / 2,610
experiments ███████████████████░ 95.4% 32,457 / 34,020
actions ███████████████████░ 95.5% 756 / 792
logs ███████████████████░ 95.5% 15,399 / 16,130
data_catalog ███████████████████░ 95.6% 4,402 / 4,606
tracing ███████████████████░ 95.6% 3,536 / 3,699
replay_vision ███████████████████░ 95.6% 27,675 / 28,939
growth ███████████████████░ 95.7% 11,228 / 11,734
messaging ███████████████████░ 95.8% 3,798 / 3,963
skills ███████████████████░ 95.8% 6,972 / 7,274
product_analytics ███████████████████░ 96.0% 28,470 / 29,647
access_control ███████████████████░ 96.3% 7,113 / 7,386
revenue_analytics ███████████████████░ 96.4% 1,876 / 1,946
user_interviews ███████████████████░ 96.5% 2,867 / 2,971
feature_flags ███████████████████░ 96.5% 25,588 / 26,509
warehouse_sources ███████████████████░ 97.3% 458,756 / 471,611
data_quality ████████████████████ 97.6% 7,587 / 7,774
metrics ████████████████████ 98.0% 4,084 / 4,166
analytics_platform ████████████████████ 98.3% 2,784 / 2,833
pulse ████████████████████ 98.5% 2,046 / 2,078
live_debugger ████████████████████ 99.2% 626 / 631

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.

@coderabbitai

coderabbitai Bot commented Sep 29, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

📝 Walkthrough

Walkthrough

The change adds Kapa AI endpoint definitions and a source that validates credentials, retrieves data through REST resources, and supports resumable pagination. The source configuration includes API-key and project-ID fields and is marked alpha. Tests cover pagination, resume state, credential validation, and error handling. The source inventory now lists Kapa AI as implemented.

Priority: ➖ Normal

Merge Risk: 🟡 Moderate · up to 0edc3

A failed finalization on a Kapa AI import can leave a saved completed marker, so the retry imports nothing and the refresh may be skipped or receive empty input. A type-check error in the tests is also failing CI. Fix both before merging.

Security Architecture Review

Security architecture risk: 🟡 Moderate · up to 0edc3

The integration adds a new path for importing project data. Its outbound credential controls are supported by the implementation, but safe checkpoint ownership depends on how import jobs are isolated and scheduled; that guarantee remains unverified.

Retained concerns

  • Medium · reliability · inferred: Kapa uses a checkpoint slot without an endpoint namespace or write-generation guard. If executions share a team and job ID, one can replace another's cursor or completed state, potentially skipping or replaying data. Whether that collision is reachable depends on unverified job identity and scheduling guarantees.
Security review details

Security Blast Radius

  • inferred — The new credentialed path reaches Kapa's project-scoped API and the importing team's warehouse data. The evidence does not establish broader service privileges or cross-tenant access.

Trust Boundaries and Controls

  • observed — Before sending a prepared request, the shared client rejects destinations outside the configured host, scheme, and port; disabled redirects are also explicitly rejected. The key can be attached to the in-memory prepared request before that check, but the rejected request is not transmitted.
  • observed — Credential validation checks the project ID's UUID format and probes a project-scoped endpoint. It relies on the provider's response rather than locally comparing a returned project identity; the provider's key-to-project authorization contract was not available.

Resilience and Maintainability Implications

  • inferred — Page and terminal-state ordering supports recovery after an interrupted sequential import. Safe recovery under overlapping executions remains dependent on checkpoint ownership that has not been established.

Hardening Proposals

  • proposed — Establish whether each active schema execution has an exclusive job ID. If that cannot be guaranteed, isolate checkpoint slots by schema and protect updates against competing executions.
🚥 Pre-merge checks | ✅ 1
✅ Passed checks (1 passed)
Check name Status Explanation
Description check ✅ Passed The description covers the problem, user-visible changes, testing, known live-account verification limits, release status, notifications, and documentation follow-up. Required sections are present and…
✨ Finishing Touches 💡 1
🛠️ Fix failing CI checks 💡
  • Commit to this branch
  • Create a new PR
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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/warehouse_sources/backend/temporal/data_imports/sources/kapa_ai/tests/test_kapa_ai.py-246-246 (1)

246-246: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Fix the mypy failure on RESTClient._send_request.retry.

CI fails at Line 246 with Callable has no attribute "retry". Mypy types the tenacity-decorated method as a plain Callable. Add a targeted ignore, or cast the method to Any before patch.object.

Proposed fix
-    with patch.object(RESTClient._send_request.retry, "wait", return_value=0):
+    with patch.object(RESTClient._send_request.retry, "wait", return_value=0):  # type: ignore[attr-defined]

Source: Pipeline failures


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: PostHog/posthog/.coderabbit.yaml

Review profile: QUIET

Plan: Enterprise

Run ID: 82b8f9e0-e166-48cc-892f-6d8e5f853776

📥 Commits

Reviewing files that changed from the base of the PR and between 9e8ba77 and 0edc35c.

⛔ Files ignored due to path filters (1)
  • frontend/public/services/kapa_ai.png is excluded by !**/*.png
📒 Files selected for processing (9)
  • products/warehouse_sources/backend/temporal/data_imports/sources/SOURCES.md
  • products/warehouse_sources/backend/temporal/data_imports/sources/generated_configs/kapaai.py
  • products/warehouse_sources/backend/temporal/data_imports/sources/kapa_ai/canonical_descriptions.py
  • products/warehouse_sources/backend/temporal/data_imports/sources/kapa_ai/kapa_ai.py
  • products/warehouse_sources/backend/temporal/data_imports/sources/kapa_ai/settings.py
  • products/warehouse_sources/backend/temporal/data_imports/sources/kapa_ai/source.py
  • products/warehouse_sources/backend/temporal/data_imports/sources/kapa_ai/tests/test_kapa_ai.py
  • products/warehouse_sources/backend/temporal/data_imports/sources/kapa_ai/tests/test_kapa_ai_source.py
  • products/warehouse_sources/backend/temporal/data_imports/sources/tests/test_source_catalog_invariants.py
💤 Files with no reviewable changes (1)
  • products/warehouse_sources/backend/temporal/data_imports/sources/tests/test_source_catalog_invariants.py

Included review availability: This review used your included allowance. Your plan provides up to 12 included reviews per hour; 3 remain after this review.

Comment on lines +139 to +145
def get_rows() -> Iterator[list[dict[str, Any]]]:
resume = resumable_source_manager.load_state() if resumable_source_manager.can_resume() else None
if resume and resume.completed:
return
resource = build_resource(api_key, project_id, endpoint, team_id, job_id, resumable_source_manager, resume)
yield from resource
resumable_source_manager.save_state(KapaResumeConfig(completed=True))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🔎 Supported by static analysis

🏁 Script executed:

#!/bin/bash
rg -nP -C3 'completed=True|\.completed\b' products/warehouse_sources/backend/temporal/data_imports/sources --type=py -g '!**/tests/**' | head -60
rg -nP -C3 '\bclear_state\s*\(' products/warehouse_sources/backend/temporal/data_imports --type=py -g '!**/tests/**' | head -60

Repository: PostHog/posthog

Length of output: 14940


🏁 Script executed:

#!/bin/bash
set -o pipefail
printf '%s\n' '--- Kapa source and diff ---'
sed -n '1,180p' products/warehouse_sources/backend/temporal/data_imports/sources/kapa_ai/kapa_ai.py
git diff --unified=25 cbf0c3d7ec8a7504143ddf796aed559bb532a30c9 0edc35c19f53d788edb346269512961074ddb3c9 -- products/warehouse_sources/backend/temporal/data_imports/sources/kapa_ai/kapa_ai.py
printf '%s\n' '--- manager declarations/usages ---'
rg -n -P -C5 'class ResumableSourceManager|def (save_state|load_state|clear_state|can_resume)\b|resumable_source_manager' products/warehouse_sources/backend/temporal/data_imports posthog --type=py | head -240
printf '%s\n' '--- extraction/load orchestration ---'
rg -n -P -C6 'yield from .*get_rows|get_rows\(\)|replace|load.*final|extract.*load|SourceResponse' products/warehouse_sources/backend/temporal/data_imports --type=py | head -260

Repository: PostHog/posthog

Length of output: 41483


🏁 Script executed:

#!/bin/bash
set -o pipefail
printf '%s\n' '--- resumable manager files ---'
fd -i 'resumable' products/warehouse_sources/backend/temporal/data_imports
printf '%s\n' '--- manager definitions ---'
rg -n -P -C8 'class ResumableSourceManager|def (save_state|load_state|clear_state|can_resume|commit)\b' products/warehouse_sources/backend/temporal/data_imports --type=py
printf '%s\n' '--- pipeline resume boundary ---'
rg -n -P -C14 'def _commit_resume_state|_commit_resume_state\(|staged.*resume|resume.*staged|release.*batch|held.*batch' products/warehouse_sources/backend/temporal/data_imports/pipelines --type=py
printf '%s\n' '--- row consumption and load failure boundary ---'
rg -n -P -C10 'source_response\.items|resource\.items|items\(\)|for .*batch|async for .*batch|load.*batch|consume.*batch' products/warehouse_sources/backend/temporal/data_imports/pipelines products/warehouse_sources/backend/temporal/data_imports --type=py | head -240

Repository: PostHog/posthog

Length of output: 45665


🏁 Script executed:

#!/bin/bash
set -o pipefail
printf '%s\n' '--- resumable manager ---'
cat -n products/warehouse_sources/backend/temporal/data_imports/sources/common/resumable.py
printf '%s\n' '--- pipeline resume methods ---'
rg -n -C10 '_commit_resume_state|resumable_source_manager|resume_state|source_response.items|finalize_run|prepare_run|write_batch' products/warehouse_sources/backend/temporal/data_imports/pipelines/pipeline_v3/pipeline.py products/warehouse_sources/backend/temporal/data_imports/pipelines/pipeline_v3/destinations_load --type=py | head -360
printf '%s\n' '--- V2 manager commit path ---'
rg -n -C12 'manager\.commit|resumable_source_manager\.commit|commit\(\)' products/warehouse_sources/backend/temporal/data_imports --type=py | head -220

Repository: PostHog/posthog

Length of output: 42345


🏁 Script executed:

#!/bin/bash
set -o pipefail
printf '%s\n' '--- manager implementation ---'
cat -n products/warehouse_sources/backend/temporal/data_imports/sources/common/resumable.py
printf '%s\n' '--- focused resume references ---'
rg -n -C16 'commit_resume|save_state|clear_state|load_state|can_resume|finalize_run' products/warehouse_sources/backend/temporal/data_imports/pipelines/pipeline_v3/pipeline.py products/warehouse_sources/backend/temporal/data_imports/pipelines/pipeline_v3/destinations_load products/warehouse_sources/backend/temporal/data_imports/sources/common/resumable.py --type=py | head -500

Repository: PostHog/posthog

Length of output: 42173


Clear the resumable state after the walk completes.

save_state stages completed=True, and the pipeline persists it after the rows are written. Finalization can still fail afterward. The retry then reads completed=True and yields no rows, so the replace refresh can be skipped or receive empty input.

Suggested fix
     def get_rows() -> Iterator[list[dict[str, Any]]]:
         resume = resumable_source_manager.load_state() if resumable_source_manager.can_resume() else None
-        if resume and resume.completed:
-            return
         resource = build_resource(api_key, project_id, endpoint, team_id, job_id, resumable_source_manager, resume)
         yield from resource
-        resumable_source_manager.save_state(KapaResumeConfig(completed=True))
+        resumable_source_manager.clear_state()
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
def get_rows() -> Iterator[list[dict[str, Any]]]:
resume = resumable_source_manager.load_state() if resumable_source_manager.can_resume() else None
if resume and resume.completed:
return
resource = build_resource(api_key, project_id, endpoint, team_id, job_id, resumable_source_manager, resume)
yield from resource
resumable_source_manager.save_state(KapaResumeConfig(completed=True))
def get_rows() -> Iterator[list[dict[str, Any]]]:
resume = resumable_source_manager.load_state() if resumable_source_manager.can_resume() else None
resource = build_resource(api_key, project_id, endpoint, team_id, job_id, resumable_source_manager, resume)
yield from resource
resumable_source_manager.clear_state()

@trunk-io

trunk-io Bot commented Sep 29, 2026 •

Copy link
Copy Markdown

Static Badge   Static Badge   Static Badge

View Full Report ↗︎ ⋅ Docs

This branch was successfully deployed

1 active deployment
preview-pr-108635 — 0edc35c1 Deployed Sep 29, 2026 by github-actions[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

stamphog Request AI approval (no full review)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants