Implement proposal 0044 (parallel-branches dispatch span)#103
Merged
chris-colinsky merged 3 commits intoMay 31, 2026
Conversation
Adds per-branch OTel dispatch spans to ParallelBranchesNode, mirroring the FanOut topology. Each branch gets its own dispatch span anchored on the parallel-branches node, with inner branch work parented under it. Augmentation and metadata-update lineage extend through these spans per the ancestor-chain rule (spec 0040 / 0045). Renames the existing node attribute `openarmature.branch_name` to `openarmature.node.branch_name` to align with the spec's attribute namespace conventions. Adds `openarmature.parallel_branches.branch_count` and `openarmature.parallel_branches.error_policy` to the node span. Bumps the spec pin to v0.36.0 and activates conformance fixture 038. Includes regression coverage for parent-resolution under the dispatch span, retry-middleware per-attempt synthesis, nested fan-out topology, and metadata augmentation with sibling-skip semantics.
There was a problem hiding this comment.
Pull request overview
Implements spec proposal 0044 by adding per-branch OpenTelemetry “dispatch” spans for ParallelBranchesNode, including new event payload (ParallelBranchesEventConfig), observer logic to open/close these spans, and accompanying conformance/unit test updates. It also pins the project’s spec version to v0.36.0 and updates related metadata/documentation.
Changes:
- Add
ParallelBranchesEventConfigto node events and emit it from the engine on parallel-branches node started/completed events. - Extend the OTel observer to synthesize per-branch dispatch spans, propagate augmentation lineage through them, and rename
openarmature.branch_name→openarmature.node.branch_name. - Activate OTel conformance fixture 038 and add multiple unit/conformance tests for the new topology and attributes.
Reviewed changes
Copilot reviewed 13 out of 13 changed files in this pull request and generated 4 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/unit/test_observability_otel.py | Adds unit tests covering parallel-branches dispatch spans, parenting, retry attempts, nested topology, and augmentation behavior; updates attribute name in assertions. |
| tests/test_smoke.py | Updates pinned spec version assertion to 0.36.0. |
| tests/conformance/test_observability.py | Activates fixture 038 and adds a new runner + span-tree matching helper for proposal 0044. |
| tests/conformance/test_fixture_parsing.py | Defers additional fixtures (038, 039, and prompt-management 017–031) with reasons. |
| src/openarmature/observability/otel/observer.py | Adds per-branch dispatch span state, opening/closing logic, augmentation target traversal, parent-resolution updates, and attribute rename/new attributes. |
| src/openarmature/graph/events.py | Introduces ParallelBranchesEventConfig and wires it into NodeEvent. |
| src/openarmature/graph/compiled.py | Constructs and dispatches parallel_branches_config on parallel-branches node started/completed events. |
| src/openarmature/AGENTS.md | Updates bundled spec version reference to v0.36.0. |
| src/openarmature/init.py | Bumps __spec_version__ to 0.36.0. |
| pyproject.toml | Updates tool.openarmature.spec_version to 0.36.0. |
| conformance.toml | Pins spec to v0.36.0 and marks proposal 0044 implemented. |
| CHANGELOG.md | Documents proposal 0044 and the breaking attribute rename; updates spec pin note. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
CoPilot's PR 103 review flagged three correctness issues for parallel-branches nodes nested deeper than depth 1; addressing them fully also required extending the analogous fan-out machinery and fixing two latent bugs the campsite test exposed. Resolver: replaces the four hard-coded namespace[:1] / depth-1 dispatch lookups (1a, 1a', 1c) with one prefix walk longest-to-shortest that checks per-branch, detached fan-out instance, and non-detached fan-out instance dispatch at every depth. The innermost matching dispatch span wins regardless of which type, which gives the right answer for parallel-branches nested inside a fan-out instance and the inverse. Synthesis: drops the depth == 1 gate from non-detached fan-out instance synthesis and from its companion dedup guard; the parent_node_name cache match already self-gates to the correct depth. Updates the detached-subgraph and detached-fan-out checks to match on prefix[-1] (the node-name segment) at any depth instead of prefix[0]; behavior at depth 1 is unchanged. _open_detached_subgraph_root now walks prefix-by-prefix to find its dispatch span's parent instead of hardcoding the invocation span. Scans: _find_fan_out_node_span and the parallel-branches NODE scan in _open_parallel_branches_branch_dispatch_span both relax to match by namespace only; the NODE span may legitimately carry an outer fan_out_index or branch_name when nested. Cache filter: drops the secondary branch_name is None / fan_out_index is None predicates from both cache-update gates. The *_config field is itself only populated on a NODE's own events, so the secondary check was redundant; in practice it skipped caching whenever a pb or fan-out node sat inside another pb's branch or fan-out's instance, breaking synthesis for the inner node. Branch-name validation: caches branch_names from ParallelBranchesEventConfig alongside parent_node_name; per-branch synthesis now requires event.branch_name to be in this pb's actual branch set. Prevents an inner pb's branch event from synthesizing a phantom dispatch span at an outer pb that doesn't declare that branch. NODE-ancestor guard: when the synthesis loop reaches an ancestor prefix that is already known to be a pb or fan-out NODE (via either cache), don't open a synthetic subgraph-wrapper span; the NODE's own span via _open_started_span is the correct ancestor. Conformance fixture 038 harness: the per-subgraph closure documented binding messages from the fixture but only bound stores_in. Now parses calls_llm.messages. Tests: - test_parallel_branches_inside_subgraph_wrapper_parent_topology - extends test_parallel_branches_inside_fan_out_instance to assert per-branch dispatch parents the leaf and itself parents under the parallel-branches NODE at the fan-out instance's namespace - test_fan_out_inside_subgraph_wrapper_emits_per_instance_dispatch_span - test_detached_subgraph_at_depth_two_mints_fresh_trace - test_three_deep_mixed_pb_fan_out_pb_composition
CoPilot's second review caught a symmetric mirror of the cache-update filter bug fixed in the previous commit. The completion handlers gated on ``branch_name is None and parallel_branches_config is not None`` (pb) and ``fan_out_index is None and fan_out_config is not None`` (fan-out). The ``*_config`` field is only populated on a NODE's own events, so the secondary predicate was redundant; in practice it skipped close for nested pb/fan-out and leaked the per-branch / per-instance dispatch span caches. Fix both close handlers symmetrically (CoPilot flagged the pb side only; same latent bug on the fan-out side). Add a regression test exercising pb-completion-inside-outer-pb-branch. Also fix two stale doc comments PR 9 left behind: - test_observability_otel.py: claim that fixture 038 activation is deferred is outdated; PR 9 activated it in the conformance harness. - test_observability.py: claim that the mock LLM matches by user- message content is outdated; the _handler is FIFO (its own docstring three lines down already says so).
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
Adds per-branch OTel dispatch spans to
ParallelBranchesNode, mirroring the FanOut topology introduced in earlier proposals. Each branch gets its own dispatch span anchored on the parallel-branches node, with inner branch work parented under that span. Augmentation and metadata-update lineage extend through these spans per the ancestor-chain rule.Bumps the spec pin to v0.36.0 and activates conformance fixture 038.
Changes
graph/events.py: newParallelBranchesEventConfigmirroringFanOutEventConfig;NodeEvent.parallel_branches_configfieldgraph/compiled.py: builds and shipsparallel_branches_configon dispatch start/complete fromnode.branches.keys()andnode.error_policyobservability/otel/observer.py:aug_bn-scoped eventsopenarmature.branch_nametoopenarmature.node.branch_namefor namespace alignmentopenarmature.parallel_branches.branch_countand.error_policyattributestest_observability.py; defers 039 (PR 11) and 017-031 prompt-management fixtures (PR 12) intest_fixture_parsing.pypyproject.toml,__init__.py,test_smoke.py,conformance.toml,AGENTS.mdBreaking change
openarmature.branch_nameis renamed toopenarmature.node.branch_nameon node spans. Downstream consumers reading this attribute must update their queries.Test plan
uv run pytest tests/unit/test_observability_otel.pyuv run pytest tests/conformance/test_observability.py