Repository navigation
chore: merge main into v1 (DO NOT SQUASH, lands by fast-forward) - #444
Merged
Merged
Conversation
) * fix(flags): quote semver values in local-evaluation errors with %q CodeQL's go/unsafe-quoting flags four semver errors that wrap a JSON-derived property value in hand-written single quotes, so a value containing a quote could break out of them. Every open PR fails CodeQL on these lines. All eight semver messages now use %q, so the message shows the value escaped and the four sibling messages stay consistent. Tested: go build ./... and go test ./... pass; no test asserts the message text. * test(flags): assert semver errors escape quoted values Covers the property and flag value branches of each semver operator family with a value holding both quote kinds. All eight cases fail against the old hand-quoted messages. Tested: go test ./...
…t-required events (#345) * feat(mcp): send $lib posthog-go-mcp on MCP analytics events Add an opt-in Library field to Capture and Exception. When set, it replaces SDKName as the event's $lib (and the top-level library on legacy wire shapes); $lib_version stays posthog-go's version. posthogmcp sets it to posthog-go-mcp on $mcp_tool_call and $exception, so MCP events carry their own library name without relabelling the host app's other events, per the mcp-analytics spec's "Library identity" requirement. A message without the field serializes exactly as before. Tested: - go build ./... && go vet ./... && go test -race -count=1 ./... (root): all ok, no reruns needed - posthogmcpsdk: go test -race -count=1 ./...: ok - bin/api-diff: up to date (two added fields) - New table tests: Library set/unset x Capture/Exception x caller $lib; a mixed legacy batch (Capture, Exception, Identify) keeps each $lib and BeforeSend sees the field; v1 mode records header and body; posthogmcp sends posthog-go-mcp even when ToolCall.Properties carries $lib. - BenchmarkAPIfy, 10 interleaved runs, origin/main vs head, median ns/op: low 3442 -> 3458 (+0.5%), medium 28738 -> 28728 (0.0%), high 138424 -> 138470 (0.0%) - Spec probe suite (copy, replace -> this worktree) vs the same suite on origin/main: checker output identical except the 1208 "$lib does not end with -mcp" violations, now 0; probe cases C1 (every era) and M9 flip FAIL -> PASS, nothing regresses. Reviewer notes: - API shape: an explicit field, not "a caller-supplied $lib property wins". Letting the property win would change $lib for any existing user who passes $lib today; the field is opt-in and invisible when empty. - CaptureModeAnalyticsV1 cannot honour Library: the server takes $lib from the per-request PostHog-Sdk-Info header and stamps it over any body $lib, and one request mixes events. MCP events in v1 mode still report posthog-go. Fixing that means splitting v1 batches per library in capture_v1_send.go, left out of this change. * feat(mcp): capture $mcp_unknown_tool and $mcp_input_required from posthogmcp Add CaptureUnknownTool and CaptureInputRequired to the manual API. A call that names an unregistered tool, and an input_required round the client receives, are not tool calls per the mcp-analytics spec, so they get their own events built on the same prepare, sanitize, and size-bound path as CaptureToolCall, with the shared identity properties factored into setIdentityProperties. Tested: - go build ./... && go vet ./... && go test -race -count=1 ./posthogmcp/ . : ok - posthogmcpsdk (replace -> ../): go build and go test -race: ok - bin/api-diff: up to date Reviewer notes: the go-sdk adapter that sends these events follows in a PR that bumps its posthog-go requirement once this is released. * fix(mcp): send $lib posthog-go-mcp on the optional events CaptureUnknownTool and CaptureInputRequired built their Capture without Library, so they reported posthog-go while the spec's Library identity rule covers every MCP event. The identity test now checks the serialized $lib. posthogmcp's package doc also says a v1 capture mode client reports one library name per request and needs a dedicated client to separate MCP events. Tested: go test -race -count=1 ./posthogmcp/ (failed first on $lib, then ok). * fix(mcp): drop an oversized $set before failing an optional event CaptureUnknownTool and CaptureInputRequired failed when SetProperties alone pushed the event past 102,400 bytes, while CaptureToolCall falls back to the required properties. They now try custom properties, then $set, before giving up. The package doc also says a v1 capture mode client reports posthog-go on MCP events, since the library name comes from the SDK there. Tested: go build, go vet, go test -race -count=1 ./... (root), posthogmcpsdk tests, bin/api-diff: ok. The new $set cases failed before the change. * chore(mcp): refresh the public API snapshot for the posthogmcp doc * fix(mcp): redact credentials in $mcp_input_request_methods CaptureInputRequired truncated each method string but did not sanitize it, so a caller-provided method containing a recognizable credential reached the event unchanged. Each method now goes through sanitizeString first. Tested: go test -race -count=1 ./posthogmcp/ (the new case failed first), bin/api-diff. * fix(mcp): give a sessionless call a session and drop an empty-object intent CaptureToolCall sent no $session_id when the caller passed neither a SessionID nor a conversation handle, and sent an intent of "{}" as a real intent. The spec requires a $session_id on every call, with distinct_id falling back to it, and treats an empty context as no intent. A call without a session now gets a fresh ses_<UUIDv7>, and "{}" is omitted like a blank intent. Found by the spec probe suite (cases M4, M7, X6). Tested: go test -race -count=1 ./posthogmcp/ (the new and updated cases fail without the change), posthogmcpsdk go vet and tests, bin/api-diff.
…sdk (#344) * feat(mcp): capture the calling model and client headers in posthogmcpsdk Requires posthog-go v1.29.0, whose ToolCall carries LLMModel, LLMModelSource, ClientUserAgent, and VendorClient, and resolves the TODO in observe. - Model: request _meta io.modelcontextprotocol/aiInvocation.model, then x-codex-turn-metadata.model (client_metadata), else an injected required llm_model argument (self_reported). Blank or "unknown" names no model. WithCaptureModel(false) turns off both sources and the injection. - llm_model is injected, stripped before dispatch, and kept out of $mcp_parameters exactly like context, through one analyticsArgument table, so a tool's own llm_model stays its data. - User-Agent and X-Anthropic-Client from RequestExtra.Header on HTTP. Tested: go test -race ./... in posthogmcpsdk (go-sdk v1.6.1), new parameterized TestInstrumentCapturesModel, TestStreamableHTTPCapturesClientHeaders, and TestInstrumentAdvertisesAnalyticsArguments. * fix(mcp): report the error a typed mcp.AddTool handler returned go-sdk turns a typed handler's error into an isError result and keeps the original on the result. The adapter read only the result text, so a handler that returned an UpstreamTimeout was recorded as error type Error, and the $exception lost its type, while Server.AddTool handlers kept theirs. It now uses CallToolResult.GetError() when set, so both kinds report the same way. Found by the spec probe suite (case C3, typed handler). Tested: posthogmcpsdk go vet and go test -race -count=1 ./... (the typed handler case failed first); a plain error and an error result keep type Error.
* feat(mcp): anchor conversations in posthogmcpsdk, on by default
A stateless HTTP request carries no session, so each call of one agent
conversation got its own $session_id. Following the cross-SDK spec, the
adapter now advertises an optional conversation_id argument and:
- keeps an echoed handle only if it is UUIDv7-shaped, lowercased; it wins
over a transport session, sets ToolCall.ConversationID, and core derives
$session_id from it (ses_ + two FNV-1a lanes, the spec vector).
- mints a UUIDv7 only when the call has neither a valid handle nor a
transport session (an Mcp-Session-Id, or a one-client connection that
go-sdk serves without RequestExtra), and appends it to the result once
as a {"conversation_id":...} text block, isError results included.
- mirrors the handle into structuredContent._mcp_instructions on every
result of tools whose plain-object output schema it declared that on.
- forgets a minted handle the result cannot carry (protocol error, or a
go-sdk v1.8 input_required round, detected by field name so v1.6.1
still builds), so no event names a conversation the agent never got.
- strips conversation_id before validation and $mcp_parameters; a tool's
own conversation_id stays its data. WithConversationID(false) opts out.
Error text and $mcp_response are read from the tool's own result, before
the handle block is appended.
Tested in posthogmcpsdk against go-sdk v1.6.1 and v1.8.0 (go get + tidy,
then restored): go build, go vet, staticcheck, go test -race ./... all
pass; the input_required test skips on v1.6.1 and fails on v1.8.0 when
the guard is removed. Pinned posthog-go v1.29.0 (replace dropped) builds
and vets on both.
* fix(mcp): anchor stateless clients that drop Mcp-Session-Id in posthogmcpsdk
Review fixes on conversation anchoring:
- A stateless go-sdk server gives a request without Mcp-Session-Id a random
session id per request. Only a one-client connection or a request that sends
the header now counts as a transport session, so such clients get a
conversation handle and a fresh ses_<UUIDv7> instead of a hash of a random id.
- A handler error sends no result, so a minted handle is neither delivered
nor recorded.
- Only a type "object" output schema gets _mcp_instructions; array and untyped
schemas (allowed by go-sdk v1.8) are left alone.
- Skip session resolution when a handle sets $session_id.
- README: the handle rides structured results only when one exists, and
$mcp_response is the tool's own result without the handle.
Tested: new table rows failed first (dropped session id on v1.6.1, handler
error with result, array/untyped output schema on v1.8.0), then pass. On
go-sdk v1.6.1 and v1.8.0: go vet, go test -race -count=1 ./..., staticcheck
clean; go.mod/go.sum restored to v1.6.1.
Reviewer note: an input_required round still emits one $mcp_tool_call, which
the spec says it should not; the test now pins that count of 1.
* fix(mcp): keep untyped input schemas extensible and leave constrained output schemas alone
withArguments accepted an input schema with properties but no explicit type
before the conversation work required type object for every schema, so such
tools lost their context, llm_model, and conversation_id arguments. Input
schemas may omit the type again. An output schema still must declare type
object, and now also must not set maxProperties or propertyNames, because
adding _mcp_instructions to a delivered result would break the schema the
client validates it against.
Tested: posthogmcpsdk go vet and go test -race -count=1 ./... ; the new
table cases for untyped inputs and constrained outputs failed first.
…gmcpsdk (#347) * feat(mcp): send $mcp_unknown_tool and $mcp_input_required from posthogmcpsdk The go-sdk adapter no longer counts an unregistered-tool call or an input_required round as $mcp_tool_call. Unknown tools are recognised by go-sdk's own unknown tool error, an echoed conversation handle still joins the $mcp_unknown_tool event, and a legacy call that exhausts go-sdk's shim is a failed call with $mcp_error_type input_required. Tested: posthogmcpsdk go vet and go test -race on go-sdk v1.6.1 and v1.8.0 (round tests skip on v1.6.1), root go vet and posthogmcp tests, bin/api-diff, probe cases X1 and C15 across eras. Reviewer notes: depends on the core PR (#345) being released; the adapter's posthog-go requirement is bumped to that release before this leaves draft. * fix(mcp): tell a forwarded unknown tool error and a failed round from the real thing A registered tool that forwards another tool's unknown tool error was recorded as $mcp_unknown_tool, losing the failed call. The adapter now requires go-sdk's message to name the requested tool. A result that asks for input next to an error is a failed call, not a round the client receives. Tested: posthogmcpsdk go vet and go test -race -count=1 ./... on go-sdk v1.6.1 and v1.8.0 (the new cases failed first on v1.8.0); root posthogmcp tests; bin/api-diff. * chore(mcp): require posthog-go v1.30.0 in posthogmcpsdk The adapter needs the manual events API from #345, released in v1.30.0. Tested: CI's build, vet and go test -race against the release with the replace dropped; go vet and go test -race in posthogmcpsdk.
* feat(mcp): add CaptureMissingCapability to posthogmcp Sends $mcp_missing_capability for an agent's report of a capability the server lacks, per the mcp-analytics spec: $mcp_resource_name is the virtual tool's name, the report is $mcp_intent (source context_parameter), and the event carries the model, conversation, session and identity properties but no $mcp_tool_name, $mcp_parameters or $mcp_duration_ms. buildOptionalEvent no longer sets $mcp_tool_name itself; $mcp_unknown_tool and $mcp_input_required pass it. Tested: go vet ./..., go test -race ./posthogmcp/..., bin/api-diff. * chore(mcp): drop comments that restate the code Remove the setModelAndIntentProperties doc and trim two added doc comments.
* docs: Refresh and simplify SDK agent guidance * docs: Match pinned-core validation flags to CI
…osthogmcpsdk (#351) * feat(mcp): add the opt-in get_more_tools missing-capability tool to posthogmcpsdk WithMissingCapabilityTool advertises a get_more_tools virtual tool on the last tools/list page unless the server registers that name. It takes the enabled llm_model and conversation_id arguments. A call to it is answered by the middleware with the canned text and sends $mcp_missing_capability (intent = context), never $mcp_tool_call or $mcp_unknown_tool, also when the catalog lookup failed. Off by default. go.mod pins posthog-go to the head of #350, which adds CaptureMissingCapability. Replace it with the release that carries #350 before merging. Tested: go vet and go test -race in posthogmcpsdk on go-sdk v1.8.0 and v1.6.1; CI's build and vet against the pinned posthog-go with the replace dropped. * fix(mcp): send the missing-capability call down the middleware chain The middleware answered the virtual tool before calling next, so authentication and rate limiting installed inside Instrument never saw it, and a server tool of that name could be shadowed when the catalog lookup failed. The call now goes to next, and the middleware replies itself only when go-sdk answers it as an unknown tool, which makes go-sdk's own registry the source of truth. Tested: go vet and go test -race in posthogmcpsdk; the two new tests (inner middleware rejects the call; server tool of that name with a failing listing) fail without the change; CI's pinned build with the replace dropped. * chore(mcp): drop comments that restate the code Remove added comments that repeat names or code; keep the go-sdk unknown-tool note and the exported option doc. * chore(mcp): require posthog-go v1.32.0 in posthogmcpsdk v1.32.0 carries CaptureMissingCapability (#350), which the adapter calls. Tested: go vet and go test -race in posthogmcpsdk; CI's build, vet and go test against v1.32.0 with the replace dropped.
…test-sdk-action.yml from 03d972e49be84402c491324320b0a0f38c2ddc53 to 27806196fa5c5ac74e9e7500bc46b74db1869f3a in the github-actions group (#364) chore(deps): bump PostHog/posthog-sdk-test-harness/.github/workflows/test-sdk-action.yml Bumps the github-actions group with 1 update: [PostHog/posthog-sdk-test-harness/.github/workflows/test-sdk-action.yml](https://github.com/posthog/posthog-sdk-test-harness). Updates `PostHog/posthog-sdk-test-harness/.github/workflows/test-sdk-action.yml` from 03d972e49be84402c491324320b0a0f38c2ddc53 to 27806196fa5c5ac74e9e7500bc46b74db1869f3a - [Release notes](https://github.com/posthog/posthog-sdk-test-harness/releases) - [Changelog](https://github.com/PostHog/posthog-sdk-test-harness/blob/main/CHANGELOG.md) - [Commits](PostHog/posthog-sdk-test-harness@03d972e...2780619) --- updated-dependencies: - dependency-name: PostHog/posthog-sdk-test-harness/.github/workflows/test-sdk-action.yml dependency-version: 27806196fa5c5ac74e9e7500bc46b74db1869f3a dependency-type: direct:production dependency-group: github-actions ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* feat(mcp): add WithIntentFallback to posthogmcpsdk
The TypeScript, Python, and Ruby MCP SDKs let a server supply the intent of a call whose agent sent no `context`, captured as `$mcp_intent_source: "inferred"` (sdk-specs mcp-analytics, "Configuration"). The Go adapter had no way to, so such calls had no intent. posthogmcp already accepted IntentSourceInferred; only the adapter lacked the hook.
WithIntentFallback takes `func(context.Context, *mcpsdk.CallToolRequest) (string, error)`, like WithIdentity. It runs only when the agent sent no usable context (blank, absent, or the context parameter is off) on `$mcp_tool_call`. An error or panic goes to the ErrorHandler and leaves the call without an intent. Blank and "{}" results and redaction come from posthogmcp's existing intent handling.
Tested: new TestInstrumentIntentFallback (9 cases, red before the change); go build, go vet, go test -race in posthogmcpsdk; build and vet against the pinned posthog-go with the replace dropped.
* test(mcp): pin the intent fallback's call count, request, and event scope
Review of the fallback found tests that passed without it running, an undocumented request and concurrency contract, and no check that it stays off for $mcp_unknown_tool and $mcp_missing_capability. Each case now asserts how often the fallback ran, a non-string context falls back, the callback sees the request with injected arguments, and the README example depends on the call's arguments.
Tested: go vet and go test -race in posthogmcpsdk; build and vet against the pinned posthog-go with the replace dropped.
* fix(mcp): run the intent fallback when the agent's context is "{}"
An agent that sent `"context": "{}"` counted as having stated an intent, so WithIntentFallback was skipped, and posthogmcp then dropped the "{}" as empty, leaving the call with no intent. The spec treats "{}" like a blank context, so it now falls through to the fallback.
Tested: new "empty object" case in TestInstrumentIntentFallback fails without the change; go vet and go test -race in posthogmcpsdk.
* feat: span processor request context * fix: span processor review feedback * fix: span processor review follow-ups
* fix(flags): load shared definitions without a secret key * fix(flags): make keyless cache consumers read-only
Bumps the github-actions group with 2 updates: [github/codeql-action/init](https://github.com/github/codeql-action) and [github/codeql-action/analyze](https://github.com/github/codeql-action). Updates `github/codeql-action/init` from 4.38.2 to 4.38.3 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](github/codeql-action@2892aa5...24c5418) Updates `github/codeql-action/analyze` from 4.38.2 to 4.38.3 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](github/codeql-action@2892aa5...24c5418) --- updated-dependencies: - dependency-name: github/codeql-action/init dependency-version: 4.38.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: github-actions - dependency-name: github/codeql-action/analyze dependency-version: 4.38.3 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: github-actions ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…el (#377) Bumps [google.golang.org/grpc](https://github.com/grpc/grpc-go) from 1.83.1 to 1.83.2. - [Release notes](https://github.com/grpc/grpc-go/releases) - [Commits](grpc/grpc-go@v1.83.1...v1.83.2) --- updated-dependencies: - dependency-name: google.golang.org/grpc dependency-version: 1.83.2 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…7 in /posthogmcpsdk in the go_modules group across 1 directory (#379) chore(deps): bump github.com/klauspost/compress Bumps the go_modules group with 1 update in the /posthogmcpsdk directory: [github.com/klauspost/compress](https://github.com/klauspost/compress). Updates `github.com/klauspost/compress` from 1.17.11 to 1.18.7 - [Release notes](https://github.com/klauspost/compress/releases) - [Commits](klauspost/compress@v1.17.11...v1.18.7) --- updated-dependencies: - dependency-name: github.com/klauspost/compress dependency-version: 1.18.7 dependency-type: indirect dependency-group: go_modules ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
…7 in /otel (#380) chore(deps): bump github.com/klauspost/compress in /otel Bumps [github.com/klauspost/compress](https://github.com/klauspost/compress) from 1.17.11 to 1.18.7. - [Release notes](https://github.com/klauspost/compress/releases) - [Commits](klauspost/compress@v1.17.11...v1.18.7) --- updated-dependencies: - dependency-name: github.com/klauspost/compress dependency-version: 1.18.7 dependency-type: indirect ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* ci(release): synchronize nested module core minimums * ci(release): publish core before nested module tags
…#357) * fix(mcp): set $exception_source and correct mechanism.synthetic The mcp-analytics spec requires the $exception emitted alongside a failed tool call to carry $exception_source: "mcp.tool_call", and to mark mechanism.synthetic only for an isError result that carried no thrown error. buildException set neither correctly: the source property was absent, and synthetic was hardcoded true on a path only reached for a thrown error. Also strip $exception_source from caller-supplied properties, matching how $exception_level is already protected. Generated-By: PostHog Desktop Task-Id: b4b6b8cc-da38-4c24-a978-9e2e9b5ac5f0 * fix(mcp): strip reserved exception metadata * fix(mcp): keep mechanism.synthetic true until the adapter can mark synthesized errors --------- Co-authored-by: posthog[bot] <206114724+posthog[bot]@users.noreply.github.com> Co-authored-by: Ioannis J <yiannis@posthog.com>
* feat(flags): honor experiment holdouts in local flag evaluation Local evaluation ignored `filters.holdout`, so an identifier inside an experiment's holdout was bucketed into a regular variant and disagreed with the server, silently and with no fallback. Holdout resolution now runs after the inactive-flag check and before release conditions, returning the synthetic `holdout-<id>` variant. Membership uses the flag-level bucketing identity (group key for group-aggregated flags, device id for device-bucketed flags, distinct id otherwise) and the server's hash of `holdout-<value>`: the "-" separator, not the "." used by rollout hashing, selects a different population. Percentages are clamped to 0-100 without truncating fractions, compared inclusively, and 100 includes everyone without hashing. A holdout missing `id` or `exclusion_percentage` is skipped, leaving ordinary evaluation unchanged. Tested: go build, go vet, gofmt, make api-diff, and go test -race -count=1 ./... at the repository root. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Generated-By: PostHog Desktop Task-Id: 2887d167-f4e8-446a-a056-202893f6ecd9 * fix(flags): keep device-bucketed partial holdouts inconclusive without a device id --------- Co-authored-by: posthog[bot] <206114724+posthog[bot]@users.noreply.github.com> Co-authored-by: Ioannis J <yiannis@posthog.com>
* fix: keep events when a property is NaN or Inf Identify, group identify, and exception custom properties skipped the normalization capture already uses, so one float32 NaN failed the whole event. * fix: ignore shadowed exception properties before normalizing Typed fields such as $lib win on collision and are never sent. Normalizing those custom values first dropped the exception when the ignored value could not be serialized. * fix: encode exception custom structs with encoding/json go-json ignores omitzero, so normalization was sending zero fields that the exception encoder used to omit.
Bumps the github-actions group with 2 updates: [github/codeql-action/init](https://github.com/github/codeql-action) and [github/codeql-action/analyze](https://github.com/github/codeql-action). Updates `github/codeql-action/init` from 4.39.1 to 4.39.2 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](github/codeql-action@9b32540...1fc58d0) Updates `github/codeql-action/analyze` from 4.39.1 to 4.39.2 - [Release notes](https://github.com/github/codeql-action/releases) - [Changelog](https://github.com/github/codeql-action/blob/main/CHANGELOG.md) - [Commits](github/codeql-action@9b32540...1fc58d0) --- updated-dependencies: - dependency-name: github/codeql-action/init dependency-version: 4.39.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: github-actions - dependency-name: github/codeql-action/analyze dependency-version: 4.39.2 dependency-type: direct:production update-type: version-update:semver-patch dependency-group: github-actions ... Signed-off-by: dependabot[bot] <support@github.com> Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* fix(flags): fall back for unsupported cohort group types * fix(flags): preserve decisive cohort siblings * revert(flags): restore backend cohort fallback semantics Reverse the sibling-composition behavior introduced in 38e3d22. Preserve order-specific regression tests for the agreed Rust backend contract.
…439) * ci: notify Slack when release jobs fail, are cancelled, or never run * ci: drop in-job Slack failure replies now covered by the notifier * ci: accept a lone thread artifact and gate the notifier on trusted runs
* fix(flags): decide cohort groups and ignore failed flag values A cohort group stopped at the first server-required leaf, so a later sibling that already proved the group never ran. A /flags detail with failed set was stored as a successful disable. A decisive sibling now settles the group, and a failed detail is left out of the value and reported as evaluation_failed. * fix(flags): address failed-flag review feedback --------- Co-authored-by: Manoel Aranda Neto <marandaneto@gmail.com>
* fix(flags): honor cohort not_in and condition negation Local evaluation treated a cohort not_in filter as membership, and a release condition with negation set still required the property to match. * fix(flags): keep invalid regex from matching under negation An invalid regex is a non-match for both regex and not_regex. Condition negation was complementing that failure and enabling the flag for every value. * fix(flags): ignore negation on legacy release conditions Cohort not_in still excludes members. A negation field on a legacy release condition is left unused, matching the backend. * test(flags): preserve unresolved cohort not_in results * docs(flags): clarify cohort not_in release note --------- Co-authored-by: Manoel Aranda Neto <marandaneto@gmail.com>
* docs: add Markdown SDK reference generator Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * docs: generate the reference with go/doc instead of gomarkdoc go/doc reads each exported symbol and go/doc/comment renders its doc comment as Markdown, so the generator needs no third-party tool and no regex. It only groups symbols by their posthog-capability tag in sdk-specs order and writes front matter. The release step checks out sdk-specs for the headings. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * chore: update the public API snapshot for the capability tags The snapshot includes doc comments, and the tags add a line to some of them. No signatures change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * ci: stage generated reference pages before the release commit ghcommit-action passes an untracked folder to ghcommit as one path, which fails on the first release that writes references/. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: read capability tags from docs/capabilities.json The posthog-capability lines showed up in IDE hover help for every SDK user. The generator now reads a label-to-slug map next to it, and fails on stale labels, unknown slugs, and unmapped SDK utils members. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: map EnqueueAI to capture-ai on 2.x Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * ci: add a workflow that regenerates the reference by hand The release's reference step is best-effort, so a failure never blocks a release. generate-references.yml reruns it from main on demand and commits references/ when anything changed. Same shape as posthog-js's generate-references.yml. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * ci: don't let the sdk-specs checkout block a release Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * ci: warn when the SDK reference doesn't regenerate The reference step is best-effort, so a failure leaves the release green. A ::warning:: annotation now says so and points to generate-references.yml. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: open the Go getting-started page with the steps to a first event Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(reference): fence code examples, list typed constants, create references/ - Doc-comment examples printed as indented code, which MDX doesn't support, so braces in them parsed as JSX. Print them as fenced Go blocks. - go/doc groups typed constants and variables under their type, and the type entry left them out, so values like CompressionGzip and every UnresolvedFlagReason weren't documented. Render them under the type. - Create references/ before writing, so generation works from a clean checkout. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(reference): clean up the generated Go reference - Print each declaration with only the comments of the fields and methods go/doc kept, so unexported fields' comments don't show, and drop the blank lines a filtered member leaves. - Put package constants and variables under Constants and Variables headings in SDK utils. - Keep the EnqueueClient type entry, which public signatures take. - Align columns with spaces, the way gofmt does. - Tag a code example go only when it looks like Go, and text otherwise. - Point [Name] doc links at the symbol's heading. - Drop empty parts, the brace escapes the site no longer needs, and the printer's escaped backticks. - Read the module path from go.mod, so the next major's page installs and imports .../posthog-go/v2. - Fix the Properties example, which put the dots at the start of lines. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(reference): drop the blank line nest leaves after the dropped H1 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * fix(reference): tag an example go only when it parses, and trim blanks inside parentheses A ... placeholder is read as an argument, so examples such as NewWithConfig(...) still parse as Go, and prose with a parenthesis no longer passes as code. A const block no longer opens with a blank line. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: map Client.Emails to the emails capability Main added Client.Emails, and the generator stops on a member with no capability, so the release step would skip the pages. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
* docs: generate Go SDK 1.x Markdown reference Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * docs: generate Go SDK 2.x (experimental) Markdown reference and migration guide Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com> * docs: regenerate the Go references with go/doc The 2.x page and migration guide come from v1 with the capability tags applied, and one migration line rewrapped so MDX doesn't read <cause> as a tag. Both changes still need to land on v1. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: regenerate the Go 2.x reference with a Capture AI section Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: regenerate the Go references with the getting-started intro Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: regenerate the Go references with fenced examples and typed constants The 1.x pages come from this branch. The 2.x page comes from the v1 branch with --major 2, so it also picks up that branch's current doc comments. The migration page is unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: regenerate the Go references with the cleaned-up generator The 1.x pages come from this branch. The 2.x and migration previews come from the v1 branch with --major 2, so the migration page now matches v1's current guide and the 2.x getting started installs .../posthog-go/v2. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: regenerate the Go references with stricter code tags Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * docs: regenerate the Go references with main's new APIs Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com>
… fixes, Markdown reference) Real merge, so v1's merge base with main moves and the release-day merge of v1 into main only has to cover what lands after this. v1 already carries main up to 1.39.0 through the squashed sync #405, so every file is resolved against main at 1.39.0 (e13d11c), not the old merge base: files main has not changed since 1.39.0 keep v1's version, and the rest are a three-way merge of v1, main@1.39.0 and main. A plain merge re-applied pre-1.39 main hunks that v1 adapted on purpose (capture_test.go legacy Library/CaptureMode tests, duplicated MCP declarations in posthogmcpsdk/tools.go). Resolutions: - Not carried, already covered on v1: #427 (legacy /batch/ retry clamp; v1 clamps Retry-After to MaxRetryBackoff and never waits after its last attempt), and the legacy halves of #433 in message.go and posthog.go (/batch/ sender, capture strategy switch). capture_v1*.go stay deleted. - config.go, posthog.go: main's email settings, sender, close and drain are kept; CaptureMode and the GeoIP default block stay removed (v1 sets $geoip_disable through its layers). - release.yml: v1's tagging and /v2 require, plus posthogmcpgo and main's release steps. codeql.yml: main's action pins. - otel/go.mod, posthogmcpsdk/go.mod: keep the /v2 requirement. - CHANGELOG.md, version.go: follow main (1.42.4). - api/public-api.txt: regenerated; 80 lines added, none removed, all in main's snapshot. Build-only edits to tests main added: example_emails_test.go imports /v2; emails_test.go gets the disabled client from New's two results; event_properties_json_test.go calls v1's prepareForSend(msg, logger); posthog_test.go expects v1's capture path. The #433 tests that fail here are fixed by the next commit. posthogmcpgo is ported to /v2 in a later commit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… main does (port #433) go-json reports a non-finite float64 but writes a float32 NaN or Inf as a bare token with no error. v1 normalized properties only after a go-json error, so an event whose only non-finite numbers were float32 values was queued as invalid JSON, and capture refused the whole request it was in (post-release item 18). A custom MarshalJSON returning invalid JSON did the same. - marshalEventPayload normalizes properties before every encode and checks the result as JSON, like main's prepareForSendV1 and #433. Finite events are byte-identical on the wire (TestPrepareCaptureChangesOnlyNonFiniteNumbers). - Exception custom properties are normalized with encoding/json, which honors omitzero, as 1.x encoded them. Keys the typed fields overwrite ($exception_list, and $exception_fingerprint / $debug_images when set) are skipped first, so a bad value there no longer drops the exception. - Drop main's legacy-only normalizeLegacyMessage and exceptionCustomWithoutShadowedKeys; v1 has no /batch/ messages. - Tests: float32 NaN/Inf alone on capture, identify, group identify and exception; a float32 NaN keeps the rest of the batch; invalid custom output fails only its own event with or without a non-finite number; the shadowed-key test uses v1's typed exception keys. - Migration guide and changeset describe the new behavior. Cost (BenchmarkPrepareForSendFinite / _Cardinality, mean of 6): typical 5.8 -> 10.5 us, low 14 -> 25 us, medium 149 -> 258 us, high 630 -> 1042 us, large AI event 226 -> 1089 us per event; main pays the same. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
main's #427 stopped its senders sleeping after the last failed attempt. v1's sender already returns before any wait on its last attempt; this test pins that for the analytics and AI lanes, with a long Retry-After and RetryAfter. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
posthogmcpgo arrived from main (#409) on the 1.x module paths. With the core at /v2, its tag (posthogmcpgo/v2.x.y) needs the /v2 module path. - go.mod: module github.com/posthog/posthog-go/posthogmcpgo/v2, requiring and replacing posthog-go/v2 and posthogmcpsdk/v2. - Imports, the example and the README snippet use the v2 paths and v2's New, which returns an error. - Its events get v2's per-event library pair from posthogmcp ($lib posthog-go-mcp and the SDK version); the end-to-end test asserts it. - CI builds it against the pinned core with bin/build-against-pinned-core, like otel and posthogmcpsdk, which skips with a warning until v2.0.0 is tagged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
posthogmcpsdk gained WithEventOptions in v2 (#425); the mcp-go adapter needs the same to set capture options on $mcp_tool_call and its $exception. A call without a distinct ID still sends process_person_profile false, and a resolver error or panic is reported and drops only its options. Tests cover options on the call and its exception, the personless default, and the error and panic paths. README, migration guide (module path) and a changeset describe it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Run with --major 2, so the 1.x and latest pages stay as main generated them. The 2.x page gains the Emails API this sync brings to v2; the migration page matches docs/migration-v2.md (non-finite numbers, posthogmcpgo/v2 path). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
PR overviewNo security issues were found in this pull request. Reviews
|
eli-r-ph
marked this pull request as draft
November 11, 2026 22:16
eli-r-ph
marked this pull request as ready for review
November 11, 2026 22:24
turnipdabeets
approved these changes
Nov 12, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
💡 Motivation and Context
v1is missingmain's 1.40.0-1.42.4: transactional email (Client.Emails),posthogmcpgo, the MCP spec alignment, flags fixes and the Markdown reference. This repo only squash-merges, and squashed syncs leave the merge base withmainat October, so the v2 release PR (#320) keeps conflicting (48 files).Warning
Do not squash or merge this PR with the button. After approval it lands by fast-forwarding
v1to this exact head, which keeps the merge commit. Squashing drops it, and #320 keeps conflicting.Changes:
253b8bdis a real merge ofmain(cbf49d4).v1already hadmainup to 1.39.0 from the squashed feat: sync main 1.39 into v1 #405, so every file is merged againstmain@1.39.0, not the old base. A plain merge brought back 1.x tests incapture_test.goand duplicate MCP declarations thatv1had removed. The commit message lists each resolution;git show --remerge-diff 253b8bdshows them.v1: fix: bound legacy capture retries #427 (v1clampsRetry-Afterand never waits after its last attempt) and the/batch/halves of fix: keep events when a property is NaN or Inf #433.3b35af9ports fix: keep events when a property is NaN or Inf #433 tov1's encoder, sov1serializes likemain. Properties are normalized and each event checked as JSON before it is queued.float32NaN or Inf alone made the request invalid JSON, and capture refused the whole batch (post-release item 18).encoding/jsonagain, which honorsomitzero.mainpays the same.a8c5c51adds a test that a failed final attempt reports at once on both lanes, which fix: bound legacy capture retries #427 fixed onmain.4fd41d2publishesposthogmcpgoasposthogmcpgo/v2, and34b7bc4addsposthogmcpgo.WithEventOptions, matchingposthogmcpsdk.7456de6regenerates the 2.x and migration references with--major 2.💚 How did you test it?
v1was changed onmainafter 1.39.0.api/public-api.txtadds 80 lines and removes none, all inmain's snapshot.float32NaN alone on all four message types, a NaN keeping the rest of the batch, invalid custom JSON failing only its own event, and the event options cases. Each fails when its fix is reverted.mainhas 0 conflicts.📝 Checklist
🤖 Generated with Claude Code