Skip to content

feat(otel): link a reply's trace back to its trigger (RIG-3499) - #1041

Open
rigel-mintaka wants to merge 5 commits into
mainfrom
compass-obs/rig-3499-cross-turn-link
Open

feat(otel): link a reply's trace back to its trigger (RIG-3499)#1041
rigel-mintaka wants to merge 5 commits into
mainfrom
compass-obs/rig-3499-cross-turn-link

Conversation

@rigel-mintaka

Copy link
Copy Markdown
Contributor

T5 shipped six of its seven moves; move 6, the cross-turn causal link, never
landed, so a reply chain had no recorded branch point and nothing read
CommsCallRequest.trigger_traceparent outside generated code.

RelayCommsCall had no span to hang a link on: the RunnerService door mounted
only the bearer pair. Mount otelconnect on it, outermost of that pair, so the
span envelopes the security-critical interceptors — the ordering serve.go
states for every other chain. It is inert without a provider, so it mounts
unconditionally like the rest.

The Post arm then adds a Link from a valid trigger_traceparent. A LINK, never a
parent: the reply keeps its own trace and merely references the trigger, which
is what stops a conversation growing one unbounded tree.

Parsing goes through the shipped W3C helper, but against a context stripped of
its span. Reusing it on the handler ctx looks equivalent and is not: that helper
returns its input unchanged on malformed input, so the local span survives, is
valid, and the reply links TO ITSELF while passing an IsValid check. The
malformed, bad-hex and all-zero cases pin that.

Un-skip the two E2E assertions this blocked: the origin span is a fresh root,
and a triggered reply starts a new trace carrying exactly one link back.

Co-authored-by: Matt Wilkinson matt@rigel.build

@linear-code

linear-code Bot commented Sep 9, 2026

Copy link
Copy Markdown

RIG-3499

@trunk-io

trunk-io Bot commented Sep 9, 2026

Copy link
Copy Markdown

Merging to main 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

github-actions Bot commented Sep 9, 2026

Copy link
Copy Markdown

Compass engineering docs preview: https://compass-obs-rig-3499-cross-t.compass-eng-docs.pages.dev

Deployed from compass-obs/rig-3499-cross-turn-link at 8dedd01.

rigel-mintaka and others added 3 commits September 9, 2026 02:09
T5 shipped six of its seven moves; move 6, the cross-turn causal link, never
landed, so a reply chain had no recorded branch point and nothing read
CommsCallRequest.trigger_traceparent outside generated code.

RelayCommsCall had no span to hang a link on: the RunnerService door mounted
only the bearer pair. Mount otelconnect on it, outermost of that pair, so the
span envelopes the security-critical interceptors — the ordering serve.go
states for every other chain. It is inert without a provider, so it mounts
unconditionally like the rest.

The Post arm then adds a Link from a valid trigger_traceparent. A LINK, never a
parent: the reply keeps its own trace and merely references the trigger, which
is what stops a conversation growing one unbounded tree.

Parsing goes through the shipped W3C helper, but against a context stripped of
its span. Reusing it on the handler ctx looks equivalent and is not: that helper
returns its input unchanged on malformed input, so the local span survives, is
valid, and the reply links TO ITSELF while passing an IsValid check. The
malformed, bad-hex and all-zero cases pin that.

Un-skip the two E2E assertions this blocked: the origin span is a fresh root,
and a triggered reply starts a new trace carrying exactly one link back.

Co-authored-by: Matt Wilkinson <matt@rigel.build>
Review round 1 found the origin span carries a link nobody wrote: otelconnect's
server branch mints one from the inbound transport context whenever the caller
propagated a traceparent, and the production Runner's client interceptor always
does (runner.go:107-115, otelconnect interceptor.go:110-117). Measured: 2 links
with a trigger, 1 with an empty one.

The E2E fixture could not see it. It mounted otelconnect on the server only, so
no traceparent ever arrived — the span's links were exactly what linkTrigger put
there, and its fresh-root assertion passed because nobody offered a parent
rather than because the door refuses one. Mount the client interceptor too,
mirroring the real ServerLink client.

Then stop reading the trigger link by position. Stamp it
compass.link.kind=cross_turn_trigger and select on that, so both the assertion
and any trace consumer stay right whatever else links the span; the negative
control counts cross-turn links, not all links.

The fresh-root invariant itself is unaffected: trustRemote is false, so
otelconnect uses WithNewRoot and the reply never nests. Whether the record's
'empty adds no link' should be read as the span's total link set is a question
about the frozen record, filed rather than decided here.

Co-authored-by: Matt Wilkinson <matt@rigel.build>
Review round 2: the client interceptor that makes (h) and (i) non-vacuous was
itself undefended. Remove it and every assertion stayed green — the parent check
passes either way, and the attribute selector matches with or without a
transport link — so the fix silently reverted to the topology that never ships.
Assert the transport link's presence in (h), where the empty trigger means
linkTrigger adds nothing and the count isolates it. Verified by dropping the
client interceptor: the suite now reddens.

(h)'s comment still claimed the Runner propagates no traceparent. It does; the
span is a root because the door refuses the offered parent, which is the
invariant worth stating — the old wording would survive a trustRemote flip, the
exact mutation that must redden.

Cite linkTrigger and Hub.RelayCommsCall by symbol: this PR's own doc-comment
insertions had already pushed the line numbers off, one of them into prose.

Note that the discriminator lives in link attributes, so zeroing the link
attribute limit exports the edge without its kind.

Co-authored-by: Matt Wilkinson <matt@rigel.build>
@rigel-mintaka
rigel-mintaka force-pushed the compass-obs/rig-3499-cross-turn-link branch from e4a5c41 to a2c67a0 Compare September 9, 2026 06:15
rigel-mintaka and others added 2 commits September 9, 2026 02:52
This PR's insertions moved relay_comms.go by three lines, which silently broke
three citations pointing into it from other files: the reverse-binding note in
store, and the sweep-enqueue and promote-deletes-the-binding notes in the E2E
test. All three now name symbols.

Checking them turned up a fourth error in the prose itself: the sweep note
credited OnSessionStarted to a Hub.Start that does not exist. The call is in
promoteSession.

The remaining line-number citations into this file live in merged design
records and were already stale before this change; they are not this PR's to
rewrite.

Co-authored-by: Matt Wilkinson <matt@rigel.build>
Round 3 lows. The (h) comment said the Runner propagates a traceparent
unconditionally; otelconnect injects only what the propagator put in ctx, and
the propagator is installed on the tracing-enabled path, so an untraced
deployment mounts the interceptor and propagates nothing.

The link-attribute-limit note lived only on the production constant, but the
test helpers are what break if the attribute is stripped: the link survives and
attribute selection then finds nothing. Say so where the selection happens.

Co-authored-by: Matt Wilkinson <matt@rigel.build>
@rigel-mintaka
rigel-mintaka marked this pull request as ready for review September 9, 2026 07:26
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.

1 participant