77
88These tests fill the gaps the conformance harness defers:
99
10- - §6 TracerProvider isolation — the load-bearing "spans don't leak
10+ - TracerProvider isolation — the load-bearing "spans don't leak
1111 into the OTel global provider" guarantee.
12- - §5 attribute population on every span type.
13- - §4.2 status mapping for every §4 error category.
14- - §5.5 LLM provider span via the ContextVar dispatch hook (queue-
12+ - attribute population on every span type.
13+ - status mapping for every error category.
14+ - LLM provider span via the ContextVar dispatch hook (queue-
1515 mediated; no synchronous direct dispatch).
16- - §4.4 detached trace mode key separation in the span stack.
17- - §10.8 checkpoint_saved → ``openarmature.checkpoint.save`` zero-
16+ - detached trace mode key separation in the span stack.
17+ - checkpoint_saved → ``openarmature.checkpoint.save`` zero-
1818 duration span.
19- - §7 log bridge filter + correlation_id injection.
19+ - log bridge filter + correlation_id injection.
2020"""
2121
2222from __future__ import annotations
@@ -121,7 +121,7 @@ def _reset_otel_global_tracer_provider(restore_to: object) -> None:
121121
122122
123123async def test_observer_uses_private_provider_not_global () -> None :
124- """Spec §6 TracerProvider isolation: the OTelObserver MUST use a
124+ """TracerProvider isolation: the OTelObserver MUST use a
125125 PRIVATE TracerProvider; spans MUST NOT appear on the OTel global
126126 provider's exporter (this is the load-bearing guarantee against
127127 duplicate spans from external auto-instrumentation libraries)."""
@@ -159,8 +159,8 @@ async def test_observer_uses_private_provider_not_global() -> None:
159159
160160
161161async def test_node_span_carries_required_attributes () -> None :
162- """Spec §5.2: every node span MUST carry the four
163- ``openarmature.node.*`` base attributes."""
162+ """Every node span MUST carry the four ``openarmature.node.*``
163+ base attributes."""
164164 g , exporter = _build_linear_graph ()
165165 await g .invoke (_LinearState (), correlation_id = "test-cid" ) # type: ignore[attr-defined]
166166 await g .drain () # type: ignore[attr-defined]
@@ -178,8 +178,8 @@ async def test_node_span_carries_required_attributes() -> None:
178178
179179
180180async def test_invocation_span_carries_required_attributes () -> None :
181- """Spec §5.1: invocation span MUST carry
182- ``openarmature.graph.entry_node`` + ``openarmature.graph. spec_version``."""
181+ """Invocation span MUST carry ``openarmature.graph.entry_node`` +
182+ ``openarmature.graph.spec_version``."""
183183 exporter = InMemorySpanExporter ()
184184 observer = OTelObserver (span_processor = SimpleSpanProcessor (exporter ))
185185 g , _ = _build_linear_graph (observer )
@@ -307,7 +307,7 @@ async def _failing_node(_s: _FailState) -> dict[str, int]:
307307
308308
309309async def test_failing_node_span_carries_error_status () -> None :
310- """Spec §4.2: a node-exception failure produces a span with
310+ """A node-exception failure produces a span with
311311 ERROR status, an exception event recorded, and the
312312 ``openarmature.error.category`` attribute on the span."""
313313 from opentelemetry .trace import StatusCode
@@ -340,8 +340,7 @@ async def test_failing_node_span_carries_error_status() -> None:
340340
341341
342342async def test_checkpoint_migrate_emits_span_with_chain_metadata (tmp_path : Path ) -> None :
343- """Spec §6 cross-ref in proposal 0014: a versioned resume whose
344- migration chain runs SHOULD emit an
343+ """A versioned resume whose migration chain runs SHOULD emit an
345344 ``openarmature.checkpoint.migrate`` span carrying
346345 ``from_version`` / ``to_version`` (final) / ``chain_length``."""
347346 from openarmature .checkpoint import (
@@ -405,8 +404,8 @@ async def _noop(_s: _MigState) -> dict[str, int]:
405404
406405
407406async def test_checkpoint_migrate_span_absent_on_version_match (tmp_path : Path ) -> None :
408- """Spec §10.12.3 fast path: when the saved record's schema_version
409- equals the current state class's schema_version, the migration
407+ """Fast path: when the saved record's schema_version equals the
408+ current state class's schema_version, the migration
410409 registry is NOT consulted. The OTel observer MUST NOT emit a
411410 ``openarmature.checkpoint.migrate`` span in that case."""
412411 from openarmature .checkpoint import CheckpointRecord , SQLiteCheckpointer
@@ -454,8 +453,8 @@ async def _noop(_s: _MatchState) -> dict[str, int]:
454453
455454
456455async def test_checkpoint_save_emits_zero_duration_span () -> None :
457- """Spec §10.8: a checkpoint save SHOULD emit a §6-style observer
458- event surfaced as a span. Our implementation emits a
456+ """A checkpoint save SHOULD emit an observer event surfaced as a
457+ span. Our implementation emits a
459458 ``openarmature.checkpoint.save`` span on every save."""
460459 cp = InMemoryCheckpointer ()
461460 exporter = InMemorySpanExporter ()
@@ -493,8 +492,8 @@ async def test_checkpoint_save_emits_zero_duration_span() -> None:
493492
494493
495494async def test_active_prompt_propagates_to_llm_span_attributes () -> None :
496- """Spec prompt-management §11: when an LLM call fires inside a
497- ``with_active_prompt`` context, the OTel observer MUST surface
495+ """When an LLM call fires inside a ``with_active_prompt`` context,
496+ the OTel observer MUST surface
498497 ``openarmature.prompt.*`` attributes on the LLM-call span.
499498 ``with_active_prompt_group`` adds ``openarmature.prompt.group_name``."""
500499 from datetime import UTC , datetime
@@ -660,8 +659,8 @@ async def test_llm_span_emits_cache_creation_attribute_when_payload_carries_it()
660659
661660
662661async def test_disable_llm_spans_skips_llm_provider_span () -> None :
663- """Spec §5.5: ``disable_llm_spans=True`` MUST suppress the
664- LLM-provider span emission while leaving all other spans intact."""
662+ """``disable_llm_spans=True`` MUST suppress the LLM-provider span
663+ emission while leaving all other spans intact."""
665664 from openarmature .graph .events import NodeEvent
666665
667666 # We don't drive a real provider here; instead we emit a synthetic
@@ -845,8 +844,8 @@ async def test_llm_error_path_emits_error_span_from_typed_failed_event() -> None
845844
846845
847846def test_log_record_factory_injects_correlation_id () -> None :
848- """Spec §7: every log record emitted during an invocation MUST
849- carry ``openarmature.correlation_id``. The bridge installs a
847+ """Every log record emitted during an invocation MUST carry
848+ ``openarmature.correlation_id``. The bridge installs a
850849 process-global :class:`logging.LogRecord` factory (rather than
851850 a logger-level filter) so the attribute lands on every record
852851 regardless of which logger originated it — Python's logging
@@ -1028,8 +1027,8 @@ def test_install_log_bridge_adds_handler_when_pre_attached_uses_different_provid
10281027
10291028
10301029def test_log_bridge_exports_records_with_correlation_id () -> None :
1031- """Spec §7 end -to-end: a log record emitted on a CHILD logger
1032- under ``current_correlation_id`` flows through the bridge to
1030+ """End -to-end: a log record emitted on a CHILD logger under
1031+ ``current_correlation_id`` flows through the bridge to
10331032 the OTel ``LoggerProvider``'s exporter with
10341033 ``openarmature.correlation_id`` populated. Child-logger emit
10351034 is the load-bearing case — Python's logging propagates child
@@ -1106,8 +1105,8 @@ def test_log_bridge_exports_records_with_correlation_id() -> None:
11061105
11071106async def test_shared_observer_concurrent_invocations_dont_collide () -> None :
11081107 """A single observer shared across concurrent invocations MUST
1109- keep their span trees isolated. Per spec §5.1 each invocation
1110- has its own ``invocation_id`` and therefore its own
1108+ keep their span trees isolated. Each invocation has its own
1109+ ``invocation_id`` and therefore its own
11111110 ``trace_id``; with shared internal state keyed by
11121111 ``invocation_id`` the observer no longer collides on overlapping
11131112 namespaces, no longer closes another in-flight invocation's span
@@ -1239,7 +1238,7 @@ async def _double(s: _ChildState) -> dict[str, int]:
12391238
12401239
12411240async def test_concurrent_fan_out_llm_spans_parent_under_calling_instance () -> None :
1242- """Spec §5.5 under concurrent fan-out: each instance's
1241+ """Under concurrent fan-out: each instance's
12431242 ``openarmature.llm.complete`` span MUST parent under that
12441243 instance's calling node, not a sibling instance's. The Phase 6.1
12451244 calling-node identity (namespace_prefix + attempt_index +
@@ -1362,7 +1361,7 @@ async def _ask(s: _ChildState) -> dict[str, str]:
13621361
13631362
13641363async def test_llm_call_inside_retried_node_parents_per_attempt () -> None :
1365- """Spec §5.5 under retry: when an LLM ``complete()`` call
1364+ """Under retry: when an LLM ``complete()`` call
13661365 happens inside a node body wrapped with retry middleware, each
13671366 attempt's LLM span MUST parent under THAT attempt's node span,
13681367 not a hardcoded ``attempt_index=0``. Phase 6.1's
0 commit comments