Skip to content

trunk-merge/pr-89410/864f07c1-41d9-46e5-94d4-a7a713dd5313 - #89650

Closed
trunk-io[bot] wants to merge 380 commits into
masterfrom
trunk-merge/pr-89410/864f07c1-41d9-46e5-94d4-a7a713dd5313
Closed

trunk-merge/pr-89410/864f07c1-41d9-46e5-94d4-a7a713dd5313#89650
trunk-io[bot] wants to merge 380 commits into
masterfrom
trunk-merge/pr-89410/864f07c1-41d9-46e5-94d4-a7a713dd5313

Conversation

@trunk-io

@trunk-io trunk-io Bot commented Aug 26, 2026

Copy link
Copy Markdown
Trunk Merge Pull Request Banner

This pull request was created and is being managed by Trunk Merge.

This pull request is based on the master branch at SHA c86295e66bcb7a647de548d8ff2765f62a5f1dd0.

See more details about each PR in the batch here:

When CI completes, this pull request will be closed automatically.

Pull Requests Being Tested

This pull request is testing a batch with the changes from pull requests 89410 and 88569 - batching documentation.

Dependencies

This pull request depends on the changes from pull requests 83910, 87138, 88886, 89624, 86299, 88862, and 88699.

Gilbert09 and others added 30 commits August 26, 2026 03:36
Person-property and account-property projection lookups now retry once on a
transient app-DB connection drop (OperationalError/InterfaceError), reusing
the existing aretry_on_db_connection_drop helper. Previously a stale pooled
connection propagated straight to materialize_view.py's catch-all, which
reported it to error tracking and silently skipped that run's property sync.

Generated-By: PostHog Desktop
Task-Id: f08526a4-2e19-4806-940c-1c228471fac4
Generated-By: PostHog Desktop
Task-Id: f1ac2634-4ae4-4683-92b3-54e332b641bc
Generated-By: PostHog Desktop
Task-Id: f1ac2634-4ae4-4683-92b3-54e332b641bc
Adds INGESTION_TRANSPORT=http|grpc. On grpc, each worker gets one
ordered WorkerIngest stream: enqueue order is wire order is the worker's
feed order, closing the reordering window concurrent HTTP requests
leave open.

Sends become two-phase on both transports: begin_send establishes send
order synchronously where order is decided (the consumer loop after
assignment, the serialized flush paths, the eager loop) and wait
resolves like an HTTP response. HTTP implements begin_send lazily, so
its behavior is unchanged.

A nack, stream break, or connect failure fences the lane: every
un-acked and queued sub-batch resolves as failed in enqueue order with
its messages, so the existing deferral path stashes them, holds all
newer work for those keys, and retries the failed group first. Lanes
reconnect with backoff and a bumped stream_epoch; rebalances bump
assignment_epoch — both scope the worker's feed-order sentinel.
The stream has no per-send timeout, so a worker that stops acking
(saturated, wedged, half-dead network) left un-acked sub-batches waiting
forever and wedged the whole consumer — the failure mode HTTP surfaced
as a request timeout. A watchdog now fences the lane when un-acked work
sees no ack for INGESTION_LANE_ACK_TIMEOUT_MS (default 60s): everything
outstanding fails back into the deferral path in order and re-routes.
The deadline advances on every ack, so a slow-but-progressing worker is
never fenced.
… mode

Adds GrpcPort::OffsetFromHttp (each worker's stream address is its HTTP
port plus an offset, INGESTION_WORKER_GRPC_PORT_OFFSET) so single-host
setups — the e2e harness and local dev — can address many workers on one
IP. The e2e FakeWorker now serves WorkerIngest on its HTTP port + 1, and
two e2e scenarios run the full Kafka → lane → ack → commit loop over
gRPC: per-distinct_id ordering, and a nacking worker fencing its lane
and rerouting in order.
…m setup

The worker now greets each stream with a reserved seq-0 ack to flush
response headers (see the worker-side fix); the lane ignores it instead
of fencing on an unknown seq. Connect and stream-open are now bounded by
the lane ack timeout — a worker that never greets (or a non-h2 endpoint
that accepts TCP) fences and re-routes instead of hanging the lane
forever, which is how production wedged. Lane logs carry the dialed
grpc_url so a wrong address is a one-line diagnosis.
Add SUB_BATCH_STATUS_BUSY so a worker can signal transient backpressure
distinctly from a failure. Adding an enum value is backward compatible;
a consumer that predates it treats the value as unknown. Regenerate the
Node stub, which CI requires to stay in sync with the proto.
Two lane-failure fixes on the gRPC transport:

Reaping a worker aborted its lane task, dropping any in-flight sends
unresolved. The reaper removes workers on the drain timeout whether or
not they still have work, so this lost sub-batches and forced the
consumer to crash and replay. Drop the lane instead: closing the queue
makes the runner fence the tail in order with the messages intact, the
same recovery path every other fence uses.

A busy ack (SUB_BATCH_STATUS_BUSY, or any status this consumer predates)
now fences as retriable backpressure rather than a fault, so the work
re-routes without counting the worker unhealthy and the metric records
busy instead of error. A FAILED ack still fences as a hard fault.
The worker greets each stream with a StreamReady frame and acks with
SubBatchAck inside a oneof, so lanes unwrap the frame before matching
on status.
…nds fail

Only busy signals (HTTP 503, busy lane) are backpressure. Connection
errors and 5xx are retriable too, but they are worker faults and must
still count against passive health.
Share one readiness helper between the HTTP and gRPC transports. Probes
carry a 5s timeout, run concurrently, and shutdown wins over a round in
progress, so a worker that accepts but never answers cannot stall
startup or SIGTERM.
A worker that fails sends while its readiness probe passes must degrade
through passive health; the retriable-vs-fault classification at the
consumer's send sites had no test.
ingestion-consumer now depends on ingestion-worker-proto, whose build
script runs protoc.
Closes the backtick-quoted identifier after the dataset in get_columns's
INFORMATION_SCHEMA.COLUMNS query, matching the pattern already used by
get_primary_keys and get_leading_index_columns.

Generated-By: PostHog Desktop
Task-Id: 5863a1af-274e-410a-8f01-dd6947ed6958
…line

The ack-progress watchdog used a single per-lane deadline that every OK ack
reset. With more than one un-acked sub-batch, a worker that kept acking newer
sub-batches but never the oldest pushed that deadline forward on each ack, so
the stuck send never timed out and its Kafka batch never completed.

Give each ledger entry its own deadline, armed when the sub-batch goes on the
wire, and key the watchdog on the oldest (front) entry. A stuck sub-batch now
fences on its own deadline even while its siblings keep acking, restoring the
per-send bound the HTTP timeout it replaces provided.

Adds a regression test: with the un-acked cap at 2 and a worker that acks every
sub-batch except the oldest, the stuck send fences on its own deadline while a
steady sibling feed continues.

Generated-By: PostHog Desktop
Task-Id: fd156565-dd05-4c89-8613-e97b82f92d59
The isolation skill told core test fixtures that need product rows to
reach the model with apps.get_model at module scope plus a TYPE_CHECKING
import, because tach ignores type-only imports. That is the same
dependency with the import edge removed, not a testing door: tach, mypy
and LSP stop seeing it, and snob does not select the test when the model
changes, so the break lands on master instead of on the PR. The crossings
scanner skips tests, which the docs read as permission for the pattern.

Replace the recipe with the rule the scanner's own reasoning implies:
seed product rows through the facade's write function or a product
testing door, with a normal import. architecture.md and the scanner
docstring say the same thing now. The two existing fixtures that use the
pattern (InsightViewed, Account) are cleaned up separately.
The lane held its worker URL as a String and cloned it into the ack, ledger,
and fence metric macros on the per-sub-batch and per-ack hot path, allocating
a fresh String on every emission.

Hold the URL as an Arc<str> so each clone is a single atomic increment, per the
rust/CLAUDE.md metrics-label convention. Log macros and the metric labels are
unchanged.

Generated-By: PostHog Desktop
Task-Id: fd156565-dd05-4c89-8613-e97b82f92d59
… acks and late enqueues

Two gaps let a routing key's newer messages reach the worker ahead of
older ones once a lane fenced, both live at the deployed un-acked cap
(INGESTION_WORKER_CONCURRENT_BATCHES above 1).

A later sub-batch's OK ack resolved at once, so its caller released the
key and never replayed it; when an earlier sub-batch then failed, only
the older messages were stashed and replayed, after the newer ones. The
lane now records acks in its ledger and resolves only the consecutive
acked prefix, so a fence still reaches an acked-but-unresolved tail and
replays it in order.

A fence resolved its sends before their callers had stashed the messages.
In that gap the consumer loop could still enqueue a fenced key's next
group, and the next stream sent it ahead of the stashed older group. Each
fenced send now carries a FenceGuard that the consumer drops right after
defer_failed; the lane keeps fencing every arrival until all guards from
the fence are dropped, then reconnects.

Adds regression tests for both, and the out-of-order ack test now asserts
that a later ack waits for the earlier seq.
Scaffold two user-requested data warehouse sources as hidden stubs
(unreleasedSource=True): Lovable (AI app builder) and Zenchef
(restaurant reservation and management).

Each adds the ExternalDataSourceType enum value, the schema-general.ts
entry, a registered stub source class, a generated config, an icon, and
SOURCES.md/_load_all registration. No sync logic yet.

Generated-By: PostHog Desktop
Task-Id: 0bfebe03-9beb-4353-9c84-1a47342d591c
The generated Kea logic type unions in sourceCatalogLogic.ts and sourceWizardLogic.tsx were out of sync with the new source types added to schema-general.ts/schema.json. This regenerates them (kea-typegen output) so the "schema.json up to date" CI check passes.
The story renders null by design, and `#storybook-root` is `display: inline-block` in the snapshot runtime, so the target collapsed to zero size and every screenshot attempt timed out.

Wrap the story in a sized frame, so the snapshot still covers the null branch: caption only today, and holding the trigger beside it if the staff gate ever regresses.

Also name that cause in the test runner. Playwright reported only "element is not visible" after a full 10s wait, three retries over, for both themes.

Generated-By: PostHog Desktop
Task-Id: ba5e6ec4-e9f7-4be9-99c9-5be55d33488e
A team that wants their product to work with PostHog AI had no doc to read. `products/posthog_ai/README.md` covered only the skills build, and the one skill about the agent — `implementing-agent-modes` — taught the frozen LangGraph runtime, down to a feature-flag example calling a helper that no longer exists.

The product README is now the integration guide: where PostHog AI renders, why there is no backend integration API, and the frontend seams a team actually uses — attaching context, injecting trusted instructions, reacting to the agent's tool calls, and registering tool cards. Every snippet is copied from a real caller. The run primitives are documented as an exit hatch, not a default.

`/integrating-with-posthog-ai` is the matching skill, routing to the right seam with a reference per seam for the detail and the gotchas.

The skills-build docs move to `products/posthog_ai/skills/README.md`, which also gains the `sync:skill` commands the old text was missing.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QN7YNd72fuG58XAwELsENt
trunk-io Bot added 26 commits August 26, 2026 18:18
@github-actions

github-actions Bot commented Aug 26, 2026

Copy link
Copy Markdown
Contributor

🤖 CI report

⚠️ Django migration SQL — 1 new migration to review

We've detected new migrations on this PR. Review the SQL output for each migration:

posthog/migrations/1321_organization_read_only_mcp_access.py

BEGIN;
--
-- Add field read_only_mcp_access to organization
--
ALTER TABLE "posthog_organization" ADD COLUMN "read_only_mcp_access" boolean DEFAULT false NULL;
ALTER TABLE "posthog_organization" ALTER COLUMN "read_only_mcp_access" DROP DEFAULT;
COMMIT;

Last updated: 2026-08-26 19:07 UTC (e14084a)

Django migration risk — migration analysis complete

We've analyzed your migrations for potential risks.

Summary: 1 Safe | 0 Needs Review | 0 Blocked

✅ Safe

Brief or no lock, backwards compatible

posthog.1321_organization_read_only_mcp_access
  └─ #1 ✅ AddField
     Adding nullable field requires brief lock
     model: organization, field: read_only_mcp_access

📚 How to Deploy These Changes Safely

AddField:

This operation acquires a brief lock but doesn't rewrite the table.

Deployment uses lock timeouts with automatic retries, so lock contention will cause retries rather than connection pile-up.

Last updated: 2026-08-26 19:07 UTC (e14084a)

@trunk-io trunk-io Bot closed this Aug 26, 2026
@trunk-io
trunk-io Bot deleted the trunk-merge/pr-89410/864f07c1-41d9-46e5-94d4-a7a713dd5313 branch August 26, 2026 19:08
@trunk-io

trunk-io Bot commented Aug 26, 2026

Copy link
Copy Markdown
Author

Static BadgeStatic BadgeStatic Badge

View Full Report ↗︎Docs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.