Fix observer augmentation scoping, wire 039 case 3#193
Merged
Conversation
Two observer corrections surfaced while wiring the nested-lineage conformance fixture (proposal 0045): - The augmentation walk's same-namespace arm now skips shared-parent fan-out / parallel-branches NODE spans, in both the OTel and Langfuse observers. A key set via set_invocation_metadata inside a fan-out instance or branch was wrongly applied to the shared fork NODE when the augmenting context executed at that node's own namespace, in addition to the dispatch span where it belongs. This violated the observability 3.4 MUST-NOT; the strict-ancestor arm already skipped shared parents. Per-instance / per-branch dispatch spans and lineage ancestors are unaffected. - The Langfuse per-instance fan-out dispatch synthesis and parent resolution are now prefix-general, so a fan-out nested below the top namespace level (inside a serial subgraph wrapper) gets its dispatch observation synthesized and its inner observations parented under it, matching the OTel observer.
Activate the nested-lineage fixture (039) in the Langfuse conformance runner. Case 3 (a fan-out inside a serial subgraph wrapper) is built by a dedicated hand-built runner -- the generic adapter cannot construct nested fan-out graphs -- and asserted against the fixture's expected trace. A ContextVar-scoped negative check enforces proposal 0045's MUST-NOT: an augmented key absent from an observation's expected metadata must be absent in the actual, which the subset matcher alone cannot catch. Cases 1 and 2 are temporarily deferred via _DEFERRED_CASES. Both need a shared observer fix: dispatch keys do not encode the enclosing fan-out instance, so a dispatch inside an outer instance collides across instances. Tracked as a separate effort.
There was a problem hiding this comment.
Pull request overview
This PR updates both the OTel and Langfuse observability observers to correct metadata augmentation scoping (proposal 0045) and extends the Langfuse observer’s dispatch synthesis/parenting so nested fan-outs under serial wrappers behave like the OTel observer. It also wires conformance fixture 039 case 3 through a dedicated hand-built runner and strengthens the Langfuse conformance assertions with a MUST-NOT “no leaked augmented keys” check.
Changes:
- Fix augmentation scoping so shared-parent fan-out / parallel-branches NODE spans are not augmented when the augmenter executes at the shared NODE namespace (OTel + Langfuse).
- Make Langfuse fan-out instance dispatch synthesis and parent resolution prefix-general to support nested fan-outs (e.g., under serial subgraph wrappers).
- Add fixture 039 case 3 support to the Langfuse conformance harness, including a negative MUST-NOT assertion for augmented metadata leakage.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| tests/conformance/test_observability_langfuse.py | Wires fixture 039 case 3 via a hand-built graph runner and adds ContextVar-scoped MUST-NOT checks for augmentation leakage. |
| src/openarmature/observability/otel/observer.py | Adjusts augmentation target collection to skip shared-parent fan-out/pb NODEs even when ns == aug_ns. |
| src/openarmature/observability/langfuse/observer.py | Mirrors augmentation scoping fix; generalizes fan-out dispatch synthesis and parent resolution to work at arbitrary namespace depths. |
| CHANGELOG.md | Documents the observer fixes and nested-dispatch Langfuse behavior change. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
From CoPilot review of #193: the prefix-general change over-generalized the detached subgraph / fan-out synthesis arms to any depth, but _trace_id_for still routes detached events by namespace[:1]. A nested detached fan-out would partially detach -- its dispatch in the new Trace but inner nodes in the main one. Re-gate both detached arms to depth == 1; only the non-detached fan-out arm and the dedup need to be prefix-general (what case 3 exercises). A nested detached fan-out now gets no synthesis, consistent with the prior behavior, until the deferred nested-dispatch-keying fix generalizes _trace_id_for too.
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.
What
Two observability observer corrections plus the first slice of conformance fixture 039 (nested-lineage augmentation, proposal 0045).
Augmentation MUST-NOT (both observers). The augmentation walk's same-namespace arm now skips shared-parent fan-out / parallel-branches NODE spans. A key set via
set_invocation_metadatainside a fan-out instance or a parallel-branches branch was incorrectly applied to the shared fork NODE when the augmenting context executed at that node's own namespace, in addition to the per-instance / per-branch dispatch span where it belongs. This violated observability 3.4; the strict-ancestor arm already skipped shared parents. The bug was pre-existing and cross-observer (029/030 carried it latently, masked by the subset-based conformance asserter).Langfuse nested fan-out dispatch. The Langfuse per-instance dispatch synthesis and parent resolution are now prefix-general, so a fan-out nested below the top namespace level (inside a serial subgraph wrapper) gets its dispatch observation synthesized and its inner observations parented under it, matching the OTel observer. Also closes the nested-dispatch limitation flagged on Synthesize Langfuse per-branch dispatch spans #190.
039 case 3 wired. Case 3 (a fan-out inside a serial subgraph wrapper) runs through a dedicated hand-built runner, since the generic adapter cannot construct nested fan-out graphs, and is asserted against the fixture's expected trace. A ContextVar-scoped negative check enforces 0045's MUST-NOT (an augmented key absent from an observation's expected metadata must be absent in the actual), which the subset matcher alone cannot catch.
Temporarily deferred
039 cases 1 (fan-out in fan-out) and 2 (parallel-branches in fan-out) are deferred via
_DEFERRED_CASES. Both need one shared observer fix: dispatch keys do not encode the enclosing fan-out instance, so a dispatch inside an outer fan-out instance collides across outer instances. That fix is its own focused effort (tracked) and warrants spec coordination. 039's suite result therefore covers case 3 only.Test plan
uv run pytest tests/-> 1469 passed, 410 skipped.