Skip to content

Bump spec submodule to v0.26.1 + Langfuse observer audit fixes#85

Merged
chris-colinsky merged 4 commits into
mainfrom
feature/spec-v0-26-1-bump-langfuse-fixtures
May 28, 2026
Merged

Bump spec submodule to v0.26.1 + Langfuse observer audit fixes#85
chris-colinsky merged 4 commits into
mainfrom
feature/spec-v0-26-1-bump-langfuse-fixtures

Conversation

@chris-colinsky
Copy link
Copy Markdown
Member

@chris-colinsky chris-colinsky commented May 28, 2026

Summary

Closes out the spec response on clarify-subgraph-name-semantics (Option A: subgraph_name carries the compiled subgraph's identity). Wires subgraph_identity through SubgraphNode / FanOutConfig / _InvocationContext / NodeEvent to both observers, plus two audit fixes from msg 07 of discuss-observability-langfuse-mapping and the harness extension for graph-topology fixtures.

Three of the new fixtures (031/032/033) stay deferred pending two additional spec/fixture ambiguities surfaced while implementing — see "Open questions" below; both queued in clarify-subgraph-name-semantics msg 03.

Observer bug fixes (msg 07 audit)

  1. entry_node correctness (observer.py:330) — when the outer graph's entry IS a SubgraphNode, the first event the observer sees comes from inside the subgraph. Walks back to event.namespace[0] so the outer entry name is recorded.
  2. Detached-mode link observation no longer carries subgraph_name — per msg 07, in detached mode the wrapper role migrates to the detached trace; the parent trace's link observation IS the SubgraphNode span and must not carry subgraph_name.

subgraph_identity wiring (Option A)

  • SubgraphNode.subgraph_identity: str | None = None (BC-preserving constructor kwarg)
  • FanOutConfig.subgraph_identity: str | None = None (symmetric for fan-out)
  • _InvocationContext.subgraph_identities: tuple[str | None, ...] — chain parallel to namespace_prefix; descend_into_subgraph / descend_into_fan_out_instance / descend_into_parallel_branch extend the chain
  • NodeEvent.subgraph_identities: tuple[str | None, ...] = () — observers read at _subgraph_identity_at(event, depth)
  • Both observers emit identity as metadata.subgraph_name (Langfuse) / openarmature.subgraph.name (OTel) on wrapper observations / spans, per-instance fan-out dispatch observations, and detached-trace wrapper observations
  • Detached trace's wrapper observation name now also uses the identity (falls back to wrapper node name when None)
  • Langfuse wrapper observation synthesis now carries namespace, step, attempt_index metadata (step from first inner event; attempt_index hardcoded 0)
  • Conformance adapter sets subgraph_identity from the fixture's subgraphs: block key
  • OTel test on fixture 002 updated to assert openarmature.subgraph.name == "inner" (identity) rather than "outer_sub" (wrapper node name) — old assertion was inconsistent with Option A; spec-side fixture 002 YAML doesn't assert this attribute

Harness extension

tests/conformance/test_observability_langfuse.py now handles subgraph / fan_out / detached-trace topology shapes via the cross-capability adapter.build_graph helper, plus multi-trace assertions for the langfuse_traces: (plural) shape used by 033.

conformance.toml

[proposals."0035"] with status = "not-yet", matches the 0031-0034 convention. Release PR flips them all to implemented since = "0.10.0" together.

Open questions

Two new fixture/spec ambiguities surfaced while wiring this up; both block fixture activation, both queued in coord thread clarify-subgraph-name-semantics msg 03:

  1. step value on outer_out in fixture 031. Fixture asserts step: 2, but graph-engine §6 says inner-subgraph node executions increment the same counter, so the engine emits step: 3. Likely a fixture draft artifact.
  2. namespace rewrite for detached-trace inner observations. Fixture 033 case 1 expects namespace: ["long_running_workflow", "step"] (uses subgraph identity for the wrapper position), but the engine fires ("dispatch", "step") (wrapper node name) and the observer emits verbatim. Spec needs to confirm whether the wrapper-role migration in detached mode should also rewrite namespace, or whether the fixture should match the existing convention.

Test plan

  • Full unit + conformance suite — 880 passed, 122 skipped
  • ruff check / ruff format --check clean
  • pyright clean across changed files
  • OTel-side test on fixture 002 updated to match Option A semantics
  • Un-defer 031/032/033 in a follow-up commit once spec resolves the two ambiguities in msg 03

Submodule bump v0.26.0 -> v0.26.1 picks up proposal 0035's three
Langfuse graph-topology fixtures (031 / 032 / 033). Pinned spec
version, __spec_version__, conformance.toml, and the smoke test all
move together.

Two observer bugs surfaced during the placement audit and are fixed:

  * entry_node correctness: when the outer graph's entry is a
    SubgraphNode, the first event the observer sees comes from inside
    the subgraph (event.namespace = (wrapper, inner),
    event.node_name = inner). Old code used event.node_name for both
    trace.metadata.entry_node and trace.name, producing the inner
    node's name. Walk back to event.namespace[0] so the outer entry
    name is the one recorded.

  * Detached-mode link observation no longer carries subgraph_name.
    Per discuss-observability-langfuse-mapping msg 07: in detached
    mode the wrapper role migrates to the detached trace; the parent
    trace's link observation IS the SubgraphNode span (no wrapper
    role) and must not carry subgraph_name. The detached trace's
    dispatch observation still carries it.

Harness extension: tests/conformance/test_observability_langfuse.py
now handles subgraph / fan_out / detached-trace topology shapes via
the cross-capability adapter.build_graph helper, plus multi-trace
assertions for fixtures using langfuse_traces: (plural). Both 022/023/
024 and the new topology fixtures share the same _run_case path; the
branch is on whether the fixture carries any topology constructs.

Fixtures 031 / 032 / 033 themselves stay deferred for now: they assert
metadata.subgraph_name = <subgraph identity> while both observers
emit subgraph_name = <wrapper node name>. Resolution queued in coord
thread clarify-subgraph-name-semantics; un-defer (and apply any
required impl change) once spec answers.

Manifest entry for proposal 0035 added as status=not-yet, matching
the 0031-0034 convention; the release PR will flip all five together.
Copilot AI review requested due to automatic review settings May 28, 2026 00:57
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Updates the project to spec v0.26.1 and extends the Langfuse conformance harness to support upcoming graph-topology fixtures (subgraphs, fan-out, detached traces), along with two LangfuseObserver correctness fixes found during a topology/placement audit.

Changes:

  • Bump pinned spec version references from 0.26.0 → 0.26.1 across runtime, tests, and docs.
  • Extend tests/conformance/test_observability_langfuse.py to build topology-shaped graphs via the shared conformance adapter and to assert multi-trace (langfuse_traces) fixtures.
  • Fix LangfuseObserver trace entry naming for “entry is a SubgraphNode” and adjust detached-mode link observation metadata (no subgraph_name on the parent-trace link observation).

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
tests/test_smoke.py Updates the expected __spec_version__ to 0.26.1.
tests/conformance/test_observability_langfuse.py Adds topology-graph build path + multi-trace assertions and generalized placeholder handling.
tests/conformance/test_fixture_parsing.py Defers new Langfuse topology fixtures from the typed round-trip parser (handled by Langfuse-specific harness).
src/openarmature/observability/langfuse/observer.py Fixes entry node naming when entry is a subgraph; adjusts detached-mode link metadata.
src/openarmature/AGENTS.md Updates embedded spec version reference to 0.26.1.
src/openarmature/init.py Bumps __spec_version__ constant to 0.26.1.
pyproject.toml Bumps [tool.openarmature].spec_version to 0.26.1.
conformance.toml Adds proposal 0035 as not-yet in the v0.26.1 batch commentary.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tests/conformance/test_observability_langfuse.py Outdated
Comment thread tests/conformance/test_observability_langfuse.py Outdated
Comment thread src/openarmature/observability/langfuse/observer.py
Comment thread src/openarmature/observability/langfuse/observer.py
Implements the coord-thread `clarify-subgraph-name-semantics`
resolution (msg 02): `metadata.subgraph_name` carries the compiled
subgraph's identity rather than the wrapper node name.

Engine surface:

- `SubgraphNode` gains `subgraph_identity: str | None = None`
  (optional, BC-preserving).
- `FanOutConfig` gains `subgraph_identity: str | None = None` for
  the symmetric fan-out case; threaded through
  `GraphBuilder.add_fan_out_node`.
- `_InvocationContext` gains `subgraph_identities:
  tuple[str | None, ...]`, parallel to `namespace_prefix` — index
  `i` is the identity of the wrapper at `namespace_prefix[i]`.
  `descend_into_subgraph` / `descend_into_fan_out_instance` /
  `descend_into_parallel_branch` extend the chain.
- `NodeEvent.subgraph_identities: tuple[str | None, ...] = ()`
  carries the chain at event-emission time so observers can read
  the identity at each wrapper depth.

Observer surface:

- Langfuse `LangfuseObserver` and OTel `OTelObserver` both gain
  `_subgraph_identity_at(event, depth)` and emit identity (or empty
  string when None) as `metadata.subgraph_name` / `openarmature.
  subgraph.name` on wrapper observations / spans, per-instance
  fan-out dispatch observations, and detached-trace wrapper
  observations.
- Detached subgraph's wrapper observation name now also uses the
  identity (falling back to the wrapper node name when None).
- Langfuse wrapper observation synthesis now carries `namespace`,
  `step`, and `attempt_index` metadata (step from the first inner
  event; attempt_index hardcoded 0 since wrappers don't retry).

Conformance adapter:

- `_TracingSubgraphNode` and `_add_fan_out_node` set
  `subgraph_identity` from the fixture's `subgraphs:` block key
  when compiling, so every fixture-built subgraph carries its
  declared identity.

Updates the pre-existing OTel-side test on fixture 002 to assert
`openarmature.subgraph.name == "inner"` (identity) rather than
`"outer_sub"` (wrapper node name) — the prior assertion was
inconsistent with Option A semantics; the spec-side fixture 002
YAML doesn't assert this attribute, so the python test was a
stricter local check that needed correction.

Langfuse fixtures 031/032/033 stay deferred pending two additional
spec/fixture ambiguities surfaced while wiring this up: (a) the
`step` value on `outer_out` (fixture 031 says 2, but graph-engine
§6 says inner-subgraph node executions increment the same counter
so the engine emits 3) and (b) whether the `namespace` metadata
on detached-trace inner observations should be rewritten to use
the subgraph identity at the wrapper position. Both queued in
coord thread `clarify-subgraph-name-semantics` msg 03.
Updates the module-header comment in
tests/conformance/test_observability_langfuse.py to clarify that
the harness *infrastructure* supports the 031/032/033 topology
shapes while activation is currently deferred. Drops the
forward-reference to fixture 027 in an internal comment that
referenced fixtures this module doesn't currently run.

Adds two focused unit tests for the observer behavior changes that
were only exercised by deferred conformance fixtures:

- test_entry_node_resolves_to_wrapper_when_entry_is_subgraph pins
  the entry_node correctness fix: when the outer entry IS a
  SubgraphNode, the first event arrives from inside the subgraph
  (event.namespace = (wrapper, inner), node_name = inner). Both
  trace.name and trace.metadata.entry_node must resolve to the
  wrapper, not the inner node.

- test_detached_subgraph_subgraph_name_placement pins the audit
  fix from coord-thread msg 07: in detached mode the wrapper role
  migrates to the detached trace. The parent trace's link
  observation must not carry subgraph_name; the detached trace's
  dispatch observation must.

Both tests sit alongside the existing dispatch-synthesis tests.
Copilot AI review requested due to automatic review settings May 28, 2026 01:36
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 19 out of 19 changed files in this pull request and generated 2 comments.

Comment thread src/openarmature/observability/langfuse/observer.py
Comment thread tests/conformance/test_observability_langfuse.py
Clarifies the detached-trace wrapper comment in the Langfuse
observer to make the BC-path asymmetry explicit: the observation
NAME falls back to the wrapper node name when subgraph_identity
is empty (UX choice — better than an empty-string observation
name), but metadata.subgraph_name stays empty per §5.3's "empty
string when no identity is tracked" contract. This lets dashboard
filters on `metadata.subgraph_name == "X"` match only wrappers
explicitly registered with `subgraph_identity = "X"`, not every
wrapper that happens to be named X.

Fixes a latent bug in the multi-trace conformance assertion path:
_assert_multi_traces was passing `expected_invariants={}` to
_assert_trace, dropping per-trace invariants like
correlation_id_consistency. Fixture 033 doesn't currently use any
per-trace invariants, so the bug was dormant — any future
multi-trace fixture using correlation_id_consistency would
silently pass when broken.

Adds _PER_TRACE_INVARIANTS to classify invariants; the multi-trace
runner filters expected_invariants to that set when delegating
per-Trace assertions, leaving cross-trace invariants to
_assert_multi_traces.
@chris-colinsky chris-colinsky merged commit 020953f into main May 28, 2026
6 checks passed
@chris-colinsky chris-colinsky deleted the feature/spec-v0-26-1-bump-langfuse-fixtures branch May 28, 2026 01:56
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.

2 participants