feat(ui): emit the PostHog session id on outbound requests (RIG-2874) - #1055
Open
rigel-mintaka wants to merge 1 commit into
Open
feat(ui): emit the PostHog session id on outbound requests (RIG-2874)#1055rigel-mintaka wants to merge 1 commit into
rigel-mintaka wants to merge 1 commit into
Conversation
|
Merging to
After your PR is submitted to the merge queue, this comment will be automatically updated with its status. If the PR fails, failure details will also be posted here |
|
Compass engineering docs preview: https://compass-ui-rig-2874-outbound-9mv2.compass-eng-docs.pages.dev Deployed from |
rigel-mintaka
force-pushed
the
compass-ui/rig-2874-outbound-impl
branch
from
September 9, 2026 22:04
53b42c4 to
9a2137b
Compare
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.
Implements RIG-2874's outbound half: the UI stamps
X-POSTHOG-SESSION-IDonoutbound gRPC-Web requests, so a PostHog session correlates with the server-side
traces it produced. The server already reads this header (#996, squash
69669259); this closes the loop.Executes the design record frozen in #1040 — tasks T1-T5. Stacked on that
bookmark rather than on
main, so it did not wait on the merge queue.What ships
packages/compass-client—sessionIdInterceptorplusisSendableSessionId: a printable-ASCII guard (/^[\x21-\x7E]+$/) capped at200 characters. The guard runs before
header.set, not after, becauseHeaders.setis a WebIDL ByteString: a well-formed value above U+00FFthrows inside the interceptor and would kill the RPC rather than merely
lose a correlation key.
callInterceptors— the early return on a missingtraceSink(
if (!traceSink) return bearer;) is replaced by an accumulating list. Underthe old shape an appended interceptor was silently skipped whenever no trace
sink was configured.
Analytics.sessionId()— re-read per request, never memoized: PostHogrotates the session id on idle and at max length, so a cached value goes
stale.
createAnalyticsnow runs beforecreateLiveClients,which deletes the ordering problem instead of working around it (no forward
let, no undefined window to guard).The four per-client factories deliberately do not grow a
sessionIdoption; the shipped path is
createLiveClients→createCompassWebTransport.That omission is documented at the call site with the audit that justifies it.
Guard exactness
Verified by exhaustive measurement, not sampling. For all 94 characters in
[0x21,0x7E]:Headers.setnever throws, and.lengthequals the UTF-8 bytelength — so the client's UTF-16 count and the server's
len()coincide onexactly the domain where the cap is applied, and
&&short-circuiting means anon-ASCII value never reaches
.lengthat all.Guard-accepted is a strict subset of server-accepted
(
TrimSpace != "" && len <= 200 && utf8.ValidString,interceptor.go:96-98),so there is no false reject. It is deliberately narrower than
Headers.set,which accepts space, tab, and DEL.
Verification
packages/compass-clientsuiteanalytics/clientsuitesbun test packagesbun test apps/ui/srcmaintsc --noEmit(both projects)moon run root:lintdevenv-cliwarnings)moon run root:markdownlintdesign-ledger-gateThe 242
apps/uifailures are pre-existing: measured on a throwaway commitoff clean
mainbefore attributing anything. This change is +6 passing, zeronew failures.
Mutation-tested, seven mutants, all caught
Green tests are not evidence a suite can fail, so each guarantee was checked by
breaking it:
id !== undefinedcheck<=→<sessionId()Two of these survived the first implementation and were found by the review
agent. The
undefinedone was not cosmetic: a bareSENDABLE.test(undefined)coerces to the string"undefined"and passes,so evaluation reaches
.lengthon undefined and throws — meaning theanalytics-off path, which is the deployment default, would have failed every
request. It went unnoticed because the case sat on the capturing seam, whose
rejects.toThrow()gate is satisfied by the interceptor's own crash. Both casesmoved to the direct seam, which asserts the awaited result is a sentinel and
therefore cannot pass if anything threw.
Review
One
reviewpass: 0 high, 3 medium, 4 low — all addressed. The mediums were thetwo surviving mutants plus unpinned composition order. The lows were stale
comments: three separate places still gave the old boot order as the
rationale for the trace-id sink, one of them claiming the slot avoided
"reordering boot" — which this change does. All three now state the surviving
reason (the trace id arrives on a reply, so there is nothing to hand over at
construction time whatever order boot runs in).
Open items for review
the record says so explicitly. It needs a boot through the TLS network
door; the dev door produces a false negative indistinguishable from a
broken UI half, and the visual smoke passes whether the reorder is right,
wrong, or absent.
getter returns undefinedcase to the capture seam(
design.md:582). That is unsafe for this implementation, since the guardcan throw on
undefined. The code is correct; only the prescribed testplacement was wrong. The record's own rule — never route a would-throw value
onto the capture seam — is right and now applied.
composeBoot(deps)seam purely so the boot order becomes testable. The record marks this as
Matt's call, so it is raised rather than decided.
RIG-2874