Implement proposal 0045 (nested-lineage augmentation)#104
Merged
Conversation
Spec proposal 0045 (observability §3.4) rewrites the per-async-context augmentation boundary as three lineage-aware rules: the augmenter's call-stack ancestor chain MUST update (every strict dispatch ancestor on the augmenter's specific path); siblings at any depth MUST NOT; shared parents (fan-out NODE, parallel-branches NODE, invocation span when inside a dispatch boundary) MUST NOT. Engine: tracks per-depth lineage chains parallel to namespace_prefix. descend_into_subgraph extends both chains by None; fan-out instance descent extends fan_out_index_chain with the instance index; pb branch descent extends branch_name_chain with the branch name. Chain ContextVars driven at every leaf-node execution site so set_invocation_metadata sees the full chain. Events: NodeEvent and MetadataAugmentationEvent grow fan_out_index_chain and branch_name_chain fields populated from context. Backwards-compatible — scalar fan_out_index and branch_name fields stay as innermost values. OTel and Langfuse observers: _collect_augmentation_targets and _handle_metadata_augmentation rewrite against the three-step boundary decision tree. Both observers store the chain on _OpenSpan / _OpenObservation so the comparison is local rather than re-derived per event. Shared-parent identification uses the parent_node_name caches structurally — pb cache mirror added to the Langfuse observer. Invocation span update: preserved per fixture 034 (outermost-serial case where the augmenter has no fan-out or pb dispatch on its call-stack path). When inside any dispatch boundary, the invocation span MUST NOT update per §3.4's shared-parent rule. Spec pin v0.36.0 → v0.37.0. Conformance fixture 039 stays deferred in the Langfuse harness — needs runtime-state item-list lookup for nested fan-outs plus a new augment_metadata_from_outer_item directive for case 2. Behavioral contract verified at unit level via test_nested_fan_out_augmentation_reaches_outer_instance_dispatch_span.
There was a problem hiding this comment.
Pull request overview
This PR implements spec proposal 0045 (observability §3.4 nested-lineage augmentation containment scope), rewriting the per-async-context augmentation boundary as three lineage-aware rules so that nested fan-outs / parallel-branches correctly route augmentation only to the augmenter's call-stack ancestor chain (skipping siblings and structural shared parents like fan-out / pb NODE spans and the invocation span when inside a dispatch boundary). The spec pin bumps v0.36.0 → v0.37.0.
Changes:
- Engine: tracks per-depth
fan_out_index_chain/branch_name_chainon_InvocationContext, extended at eachdescend_into_*, surfaced onNodeEvent/MetadataAugmentationEvent, and driven through new ContextVars at each leaf execution site soset_invocation_metadatareads the full chain. - Observers:
_OpenSpan/_OpenObservationcarry their own chains;_collect_augmentation_targets(OTel) and_handle_metadata_augmentation(Langfuse) rewrite against §3.4's three-step decision tree with chain-prefix matching and structural shared-parent skipping viaparent_node_namecaches. - Tests / version: new unit test covering nested fan-out + subgraph wrapper + leaf augmentation; conformance fixture 039 deferred in the Langfuse harness with an in-source rationale; spec pin & package metadata bumped to 0.37.0.
Reviewed changes
Copilot reviewed 17 out of 17 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| src/openarmature/graph/observer.py | Adds per-depth lineage chain fields to _InvocationContext and extends them in each descend_into_*. |
| src/openarmature/graph/parallel_branches.py | Threads branch_name keyword into descend_into_parallel_branch so the branch chain advances. |
| src/openarmature/graph/compiled.py | Imports the new chain ContextVar accessors and sets/resets them around every node execution site; propagates chains onto dispatch events. |
| src/openarmature/graph/events.py | Adds fan_out_index_chain / branch_name_chain to NodeEvent and MetadataAugmentationEvent. |
| src/openarmature/observability/correlation.py | Introduces chain ContextVars + internal setters/resetters + public current_*_chain accessors. |
| src/openarmature/observability/metadata.py | Populates the new chain fields when emitting MetadataAugmentationEvent. |
| src/openarmature/observability/otel/observer.py | Carries chains on _OpenSpan, rewrites _collect_augmentation_targets as a chain-aware §3.4 walk, adds _span_chain_on_path. |
| src/openarmature/observability/langfuse/observer.py | Mirrors the OTel rewrite for Langfuse, adds parallel_branches_parent_node_name cache and _observation_chain_on_path. |
| tests/unit/test_observability_otel.py | New regression test exercising nested fan-out + subgraph + leaf augmentation against §3.4. |
| tests/conformance/test_observability_langfuse.py | Documents why fixture 039 is deferred (two missing harness primitives). |
| tests/test_smoke.py | Updates the asserted spec version. |
| CHANGELOG.md, conformance.toml, pyproject.toml, src/openarmature/init.py, src/openarmature/AGENTS.md | Spec pin and changelog updates to 0.37.0. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
CI check_conformance_manifest.py flagged that proposal 0045 (accepted in spec v0.37.0) had no entry in conformance.toml. Add the entry alongside 0044 with status=implemented and the same v0.11.0 ``since`` as the other proposals in the batch.
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.
Summary
Implements proposal 0045 (observability §3.4 nested-lineage augmentation containment scope). Rewrites the per-async-context augmentation boundary as three lineage-aware rules: the augmenter's call-stack ancestor chain MUST update, siblings at any depth MUST NOT, and shared parents (fan-out NODE, parallel-branches NODE, invocation span when inside a dispatch boundary) MUST NOT.
Spec pin v0.36.0 → v0.37.0.
Changes
graph/observer.py,graph/parallel_branches.py,graph/compiled.py): per-depth lineage chains (fan_out_index_chain,branch_name_chain) on_InvocationContext. Eachdescend_into_*extends both chains by one entry. Four leaf-site chain ContextVar sets in compiled.py soset_invocation_metadatareads the full chain.graph/events.py): chain fields onNodeEventandMetadataAugmentationEvent, populated from context. Scalarfan_out_index/branch_namefields stay as innermost values (backwards-compatible).observability/otel/observer.py):_OpenSpancarries chains;_collect_augmentation_targetsrewritten chain-aware against the §3.4 three-step boundary tree; new_span_chain_on_pathhelper.observability/langfuse/observer.py):_OpenObservationcarries chains;_handle_metadata_augmentationrewritten; new_observation_chain_on_pathhelper;parallel_branches_parent_node_namecache added for shared-parent identification.observability/correlation.py): chain ContextVars + accessors.observability/metadata.py):set_invocation_metadatareads chain vars.observability/039-nested-lineage-augmentation) deferred in the Langfuse harness with an in-source explanation of the two harness primitives needed. Unit-level coverage intest_nested_fan_out_augmentation_reaches_outer_instance_dispatch_spanexercises the three boundary rules.Spec interpretation flag
The invocation span update behavior is preserved per fixture 034 (outermost-serial: no fan-out and no pb dispatch on the augmenter's chain). A strict reading of §3.4 would treat the invocation span as ALWAYS a shared parent regardless of cardinality, but that would break fixture 034 which 0045 explicitly says stays unchanged. Reconciliation predicate is
all(fi is None for fi in aug_fi_chain) and all(bn is None for bn in aug_bn_chain)— invocation updates only when no dispatch boundary sits on the augmenter's path. Flagged on the coord thread (discuss-augmentation-nested-lineage-scopemsg-04) for spec confirmation before the v0.11.0 release.Test plan
uv run pytest tests/unit/test_observability_otel.py tests/unit/test_observability_langfuse.pyuv run pytest tests/— 1010 passed, 208 skipped, 3 deselectedtest_nested_fan_out_augmentation_reaches_outer_instance_dispatch_spancovers two-instance fan-out + nested subgraph wrapper + lineage-aware boundary application