From 6fd75aabcffb346b68ad2e3cbb44ed5092c74cd7 Mon Sep 17 00:00:00 2001 From: chris-colinsky Date: Mon, 29 Jun 2026 10:45:55 -0700 Subject: [PATCH 1/3] Bump spec conformance pin to v0.84.0 Advance the spec submodule to v0.84.0 and defer every conformance fixture the jump pulls in for proposals not yet implemented (0062, 0077-0089), at both the parser and run layers, so the suite is green at the new baseline. Mechanical: no proposal implemented, no src behavior changed. - spec submodule + the three pin constants (pyproject, __init__, test_smoke) move 0.70.1 -> 0.84.0. - conformance.toml: spec_pin -> v0.84.0 + 14 not-yet proposal rows. - The deferral dicts in the parser-level harness and the four per-capability runners gain the new fixtures, each reason naming its proposal; AGENTS.md regenerated. - 008's detached fan-out-instance error-status case (proposal 0061, implemented) is deferred pending a harness-wiring fix tracked separately; the mechanism stays verified by the subgraph case. --- conformance.toml | 95 ++++++++++++++++++- openarmature-spec | 2 +- pyproject.toml | 2 +- src/openarmature/AGENTS.md | 23 ++--- src/openarmature/__init__.py | 2 +- tests/conformance/test_fixture_parsing.py | 72 ++++++++++++++ tests/conformance/test_llm_provider.py | 17 ++++ tests/conformance/test_observability.py | 98 ++++++++++++++++++++ tests/conformance/test_prompt_management.py | 12 +++ tests/conformance/test_retrieval_provider.py | 32 ++++++- tests/test_smoke.py | 2 +- 11 files changed, 338 insertions(+), 19 deletions(-) diff --git a/conformance.toml b/conformance.toml index eda2344..73d927c 100644 --- a/conformance.toml +++ b/conformance.toml @@ -32,7 +32,7 @@ [manifest] implementation = "openarmature-python" -spec_pin = "v0.70.1" +spec_pin = "v0.84.0" # Status values: # implemented — shipped behavior matches the proposal's contract @@ -764,3 +764,96 @@ note = "ParallelBranchesNode gains two additive branch forms. (1) Inline-callabl status = "implemented" since = "0.15.0" note = "The model's output tool calls get an output-side home on the openarmature.llm.complete span. observability §5.5.10 adds the UNGATED identity projections openarmature.llm.output.tool_calls.count / .names / .ids (the class of openarmature.llm.model / attempt_index; emitted only on a tool-calling completion, omitted entirely otherwise -- not count=0); .names and .ids are index-aligned in request order, .count equals their length. §5.5.1 adds the GATED openarmature.llm.output.tool_calls, the full [{id, name, arguments}] serialization (reusing the §5.5.5 input tool-call encoding) carrying the arguments, suppressed under disable_provider_payload and subject to the truncation contract. graph-engine §6: LlmCompletionEvent gains an output_tool_calls field (the ToolCall records, populated unconditionally). python carries the field on BOTH the terminal LlmCompletionEvent (spec-conformance + the Langfuse/consumer path) and the python-internal per-attempt LlmRetryAttemptEvent, and the OTel observer renders the span attributes from the per-attempt event (the LLM-span source since 0050) -- mirroring how output_content already works. OA-namespace, no gen_ai.* mirror (the attempt_index precedent). Langfuse request-side mapping is OUT OF SCOPE (proposal defers it as future work); no Langfuse change. Fixtures 085 (two calls -> count/names/ids), 086 (no calls -> family absent), 087 (payload-gating: identity survives off, gated full present only on)." + +# Spec v0.71.0 (proposal 0062). LLM completion streaming -- an opt-in +# stream flag on complete() emitting a per-chunk LlmTokenEvent plus the +# §6 streaming-assembly reassembly into the atomic Response. Python has +# not yet shipped streaming; v0.16.0 leaves it not-yet. llm-provider +# fixtures 059/060 and observability fixtures 111-118 defer with it. +[proposals."0062"] +status = "not-yet" + +# Spec v0.72.0 (proposal 0077). Retrieval-provider TEI wire mapping +# (§8 / §8.1) plus the input_type embedding knob (§2 / §3). Python has +# not yet shipped the retrieval wire mappings; not-yet. retrieval- +# provider fixtures 013-017 defer with it. +[proposals."0077"] +status = "not-yet" + +# Spec v0.73.0 (proposal 0078). Retrieval-provider Jina hosted wire +# mapping (§8.2). Not-yet; retrieval-provider fixtures 018-022 defer +# with it. +[proposals."0078"] +status = "not-yet" + +# Spec v0.74.0 (proposal 0079). Retrieval-provider OpenAI-compatible +# embeddings wire mapping (§8.3). Not-yet; retrieval-provider fixtures +# 023-027 defer with it. +[proposals."0079"] +status = "not-yet" + +# Spec v0.75.0 (proposal 0080). PromptGroup arity enforcement +# (prompt-management §10 / §11 -- construct-time raise plus the new +# prompt_group_invalid error category). Not-yet; prompt-management +# fixture 035 defers with it. +[proposals."0080"] +status = "not-yet" + +# Spec v0.76.0 (proposal 0081). Conformance-adapter value-matcher +# vocabulary (§5.10) -- ratifies the fixture matcher tokens already in +# use. Not-yet (no fixture changes); awaits a conformance-pin batch. +[proposals."0081"] +status = "not-yet" + +# Spec v0.77.0 (proposal 0082). Structured-output failure diagnostics +# (graph-engine §6 -- LlmFailedEvent response-side surface for +# structured_output_invalid + llm-provider §7 finish_reason / usage). +# Not-yet; llm-provider fixtures 022/023 and observability fixtures +# 120-125 defer with it. +[proposals."0082"] +status = "not-yet" + +# Spec v0.78.0 (proposal 0083). Per-prompt token-budget observability +# (prompt-management §3 / graph-engine §6 / observability §5.5.15). +# Not-yet; observability fixtures 126-131 defer with it. +[proposals."0083"] +status = "not-yet" + +# Spec v0.81.0 (proposal 0084). Nested-fan-out span lineage chain +# (graph-engine §6 fan_out_index_chain / branch_name_chain + +# observability §4 / §5.5 lineage-resolved parent). Not-yet; graph- +# engine fixture 039 and observability fixtures 132-134 (+ a new case +# on 008) defer with it. +[proposals."0084"] +status = "not-yet" + +# Spec v0.80.0 (proposal 0085). Nested-fan-out checkpoint resume +# lineage (pipeline-utilities §10.11 enclosing_fan_out_lineage). Not- +# yet; pipeline-utilities fixture 076 defers with it. +[proposals."0085"] +status = "not-yet" + +# Spec v0.79.0 (proposal 0086). Service-wide default cache_ttl_seconds +# on PromptManager (prompt-management §6). Not-yet; prompt-management +# fixture 036 defers with it. +[proposals."0086"] +status = "not-yet" + +# Spec v0.82.0 (proposal 0087). Conformance-adapter within-node +# directive execution order (§8.3). Not-yet; observability fixture 135 +# defers with it. +[proposals."0087"] +status = "not-yet" + +# Spec v0.83.0 (proposal 0088). Langfuse parallel-branches mapping +# parity (observability §8.4.8). Not-yet; observability fixture 136 +# defers with it. +[proposals."0088"] +status = "not-yet" + +# Spec v0.84.0 (proposal 0089). Embedding / rerank typed-event output +# (graph-engine §6 EmbeddingEvent.output_vectors / RerankEvent. +# output_results + observability output mappings). Not-yet; +# observability fixtures 137/138 defer with it (and re-source 083/108). +[proposals."0089"] +status = "not-yet" diff --git a/openarmature-spec b/openarmature-spec index 3eef557..7a974e0 160000 --- a/openarmature-spec +++ b/openarmature-spec @@ -1 +1 @@ -Subproject commit 3eef55792a4aaaf0f3eca6e5a1fb832d4d52cebc +Subproject commit 7a974e0b11d0a485b76a34041366612ea1e65b77 diff --git a/pyproject.toml b/pyproject.toml index 94a048b..e4fe4b0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -63,7 +63,7 @@ Specification = "https://github.com/LunarCommand/openarmature-spec" openarmature = "openarmature.cli:main" [tool.openarmature] -spec_version = "0.70.1" +spec_version = "0.84.0" [dependency-groups] dev = [ diff --git a/src/openarmature/AGENTS.md b/src/openarmature/AGENTS.md index 1ae732b..5be11e3 100644 --- a/src/openarmature/AGENTS.md +++ b/src/openarmature/AGENTS.md @@ -1,6 +1,6 @@ # OpenArmature — Agent documentation -*This is the agent guide bundled with the openarmature Python package, version 0.15.0 (spec v0.70.1). For the full docs site see [openarmature.ai](https://openarmature.ai). For the canonical spec text see [openarmature.org/capabilities](https://openarmature.org/capabilities/). For project-specific conventions for the code you're editing, see the host project's `AGENTS.md` or `CLAUDE.md`.* +*This is the agent guide bundled with the openarmature Python package, version 0.15.0 (spec v0.84.0). For the full docs site see [openarmature.ai](https://openarmature.ai). For the canonical spec text see [openarmature.org/capabilities](https://openarmature.org/capabilities/). For project-specific conventions for the code you're editing, see the host project's `AGENTS.md` or `CLAUDE.md`.* ## TL;DR @@ -10,7 +10,7 @@ OpenArmature is a workflow framework for LLM pipelines and tool-calling agents: ## Capability contracts -_Sourced from openarmature-spec v0.70.1. Each entry below reproduces §1 (Purpose) and §2 (Concepts) of the capability's `spec.md` verbatim — including additions from accepted proposals that this Python implementation may not yet ship. For per-proposal implementation status (implemented / partial / textual-only / not-yet), see the `conformance.toml` manifest at the repo root. For the full spec text (execution model, error semantics, determinism, observer hooks, etc.) see the linked docs site._ +_Sourced from openarmature-spec v0.84.0. Each entry below reproduces §1 (Purpose) and §2 (Concepts) of the capability's `spec.md` verbatim — including additions from accepted proposals that this Python implementation may not yet ship. For per-proposal implementation status (implemented / partial / textual-only / not-yet), see the `conformance.toml` manifest at the repo root. For the full spec text (execution model, error semantics, determinism, observer hooks, etc.) see the linked docs site._ ### Capability: `graph-engine` @@ -221,7 +221,7 @@ and is invisible to nodes that don't opt into middleware. **Middleware.** An async callable with the shape: -``` +```python async def middleware(state, next) -> partial_update ``` @@ -262,13 +262,14 @@ second, and so on, with the original node at the inner end. For a chain `[m1, m2, m3]` wrapping node `n`, execution proceeds: -``` -m1 sees state, calls next(s) ────► m2 sees state, calls next(s) ────► m3 sees state, calls next(s) - │ - ▼ - n(state) → partial_update - │ -m1 returns partial_update ◄──── m2 returns partial_update ◄──── m3 returns partial_update +```mermaid +flowchart LR + m1["m1 sees state,
calls next(s)"] --> m2["m2 sees state,
calls next(s)"] + m2 --> m3["m3 sees state,
calls next(s)"] + m3 --> n["n(state) produces
partial_update"] + n --> r3["m3 returns
partial_update"] + r3 --> r2["m2 returns
partial_update"] + r2 --> r1["m1 returns
partial_update"] ``` Each middleware's return value flows back through the previous layer's `next` call return. @@ -283,7 +284,7 @@ The two phases are tied to a single position in the chain: if `m1` is outermost, runs first AND `m1`'s post-phase runs last. Pre-order and post-order are not configured independently. Concretely, a middleware function carries both phases: -``` +```python async def my_middleware(state, next): # ── pre-node phase: runs on the way IN ── started_at = time.time() diff --git a/src/openarmature/__init__.py b/src/openarmature/__init__.py index 5694b8c..851813c 100644 --- a/src/openarmature/__init__.py +++ b/src/openarmature/__init__.py @@ -25,7 +25,7 @@ """ __version__ = "0.15.0" -__spec_version__ = "0.70.1" +__spec_version__ = "0.84.0" # Proposal 0052 (spec observability §5.1 / §8.4.1): canonical # package-registry name for this implementation. Surfaces on every # OTel invocation span as ``openarmature.implementation.name`` and on diff --git a/tests/conformance/test_fixture_parsing.py b/tests/conformance/test_fixture_parsing.py index 4eb5b4a..0c466f6 100644 --- a/tests/conformance/test_fixture_parsing.py +++ b/tests/conformance/test_fixture_parsing.py @@ -557,6 +557,78 @@ def _id(case: tuple[str, Path]) -> str: "observability/110-otel-callable-branch-span": ( "Cross-capability parser doesn't model final_state + span_tree together; runs in test_observability" ), + # ----- v0.16.0 spec-pin bump (v0.70.1 -> v0.84.0) ----------------------- + # New fixtures whose directive shapes the cross-capability parser doesn't + # model, for proposals deferred to their own later v0.16.0 PRs. Each runs + # (or stays accounted) in its capability runner once that proposal lands. + # Proposal 0062 (LLM completion streaming, v0.71.0) -- the stream-flag + # llm-provider wire fixtures + the per-chunk LlmTokenEvent observability + # fixtures. (117 parses cleanly and is accounted in test_observability.) + "llm-provider/059-openai-streaming-wire": "Proposal 0062 streaming; not implemented", + "llm-provider/060-stream-unsupported-mapping-rejects": "Proposal 0062 streaming; not implemented", + "observability/111-llm-token-event-dispatch-on-stream": "Proposal 0062 streaming; not implemented", + "observability/112-llm-token-event-absent-without-stream": "Proposal 0062 streaming; not implemented", + "observability/113-streamed-tool-call-reassembles-no-token-events": ( + "Proposal 0062 streaming; not implemented" + ), + "observability/114-llm-token-event-then-failure-mid-stream": "Proposal 0062 streaming; not implemented", + "observability/115-llm-token-event-call-id-links-to-completion": ( + "Proposal 0062 streaming; not implemented" + ), + "observability/116-llm-token-event-call-level-retry-one-call-id": ( + "Proposal 0062 streaming; not implemented" + ), + "observability/118-llm-token-event-reasoning-delta-kind": "Proposal 0062 streaming; not implemented", + # Proposal 0075 (callable branches) coverage round-out fixture 119 + # (v0.73.1); the cross-capability parser doesn't model its graph-style + # shape (cf. 110). Accounted in test_observability. + "observability/119-otel-callable-branch-attempt-index-under-node-retry": ( + "Proposal 0075 callable-branch coverage round-out; harness shape not modelled" + ), + # Proposal 0082 (structured-output failure diagnostics, v0.77.0) -- the + # LlmFailedEvent response-side directive shape. + "observability/120-llm-failure-event-structured-output-truncation": ( + "Proposal 0082 structured-output failure diagnostics; not implemented" + ), + "observability/121-llm-failure-event-structured-output-schema-mismatch": ( + "Proposal 0082 structured-output failure diagnostics; not implemented" + ), + "observability/122-llm-failure-event-response-side-null-on-non-body-failure": ( + "Proposal 0082 structured-output failure diagnostics; not implemented" + ), + # Proposal 0083 (per-prompt token-budget observability, v0.78.0) -- the + # token_budget directive shape + budget-exceeded expectations. + "observability/126-token-budget-input-exceeded": "Proposal 0083 token-budget; not implemented", + "observability/127-token-budget-total-exceeded": "Proposal 0083 token-budget; not implemented", + "observability/128-token-budget-under-budget-no-warning": "Proposal 0083 token-budget; not implemented", + "observability/129-token-budget-absent-unchanged": "Proposal 0083 token-budget; not implemented", + "observability/130-langfuse-token-budget-warning-level": "Proposal 0083 token-budget; not implemented", + "observability/131-token-budget-on-structured-output-failure": ( + "Proposal 0083 token-budget; not implemented" + ), + # Proposal 0084 (nested-fan-out span lineage, v0.81.0) -- the + # lineage-chain directive shapes. (132 parses cleanly; accounted in + # test_observability.) + "observability/133-otel-nested-fan-out-orphan-llm-fallback": ( + "Proposal 0084 nested-fan-out span lineage; not implemented" + ), + "observability/134-langfuse-nested-fan-out-parent-resolution": ( + "Proposal 0084 nested-fan-out span lineage; not implemented" + ), + # Proposal 0087 (within-node directive execution order, v0.82.0). + "observability/135-within-node-directive-execution-order": ( + "Proposal 0087 within-node directive execution order; not implemented" + ), + # Proposal 0089 (embedding / rerank typed-event output, v0.84.0) -- the + # rerank failure observation directive shape (rerank capability unshipped). + "observability/138-langfuse-rerank-failure-observation": ( + "Proposal 0089 rerank failure observation; rerank capability not implemented" + ), + # Proposal 0086 (PromptManager default cache_ttl_seconds, v0.79.0) -- the + # manager default-cache-ttl directive shape. + "prompt-management/036-prompt-manager-default-cache-ttl": ( + "Proposal 0086 default cache_ttl_seconds; not implemented" + ), } diff --git a/tests/conformance/test_llm_provider.py b/tests/conformance/test_llm_provider.py index c794807..3d2dc62 100644 --- a/tests/conformance/test_llm_provider.py +++ b/tests/conformance/test_llm_provider.py @@ -112,6 +112,23 @@ "056-call-level-retry-transient": ("per-attempt LLM spans; see test_observability_otel.py"), "057-call-level-retry-exhaustion": ("per-attempt LLM spans; see test_observability_otel.py"), "058-call-level-retry-non-transient-no-retry": ("per-attempt LLM spans; see test_observability_otel.py"), + # ----- v0.16.0 spec-pin bump (v0.70.1 -> v0.84.0) ------------------- + # Proposal 0082 (structured-output failure diagnostics, spec v0.77.0) + # extended fixtures 022/023 to additionally assert the now-required + # finish_reason + usage on the structured_output_invalid error; python + # does not yet carry those response-side fields on the error, so the + # extended fixtures defer until a later v0.16.0 PR lands 0082. + "022-structured-output-parse-failure": ( + "Proposal 0082 finish_reason/usage on structured_output_invalid; not implemented" + ), + "023-structured-output-validation-failure": ( + "Proposal 0082 finish_reason/usage on structured_output_invalid; not implemented" + ), + # Proposal 0062 (LLM completion streaming, spec v0.71.0) -- the stream + # flag on complete() + SSE wire handling + the streaming-unsupported + # rejection. Unimplemented until a later v0.16.0 PR. + "059-openai-streaming-wire": "Proposal 0062 streaming; not implemented", + "060-stream-unsupported-mapping-rejects": "Proposal 0062 streaming; not implemented", } diff --git a/tests/conformance/test_observability.py b/tests/conformance/test_observability.py index 73fe1ba..cc7211c 100644 --- a/tests/conformance/test_observability.py +++ b/tests/conformance/test_observability.py @@ -263,6 +263,10 @@ def _reset_otel_global_tracer_provider(restore_to: object) -> None: "082-otel-embedding-span-attributes", "083-langfuse-embedding-observation", "089-embedding-metrics-token-and-duration", + # proposal 0089 (v0.84.0) embedding failure observation -- the + # EmbeddingFailedEvent ERROR-level Langfuse rendering; blocked on + # the same unimplemented embedding capability. + "137-langfuse-embedding-failure-observation", ) }, # Rerank observability (proposal 0060, v0.70.0). The rerank protocol is @@ -281,8 +285,93 @@ def _reset_otel_global_tracer_provider(restore_to: object) -> None: "107-otel-rerank-span-attributes", "108-langfuse-rerank-observation", "109-rerank-metrics-token-and-duration", + # proposal 0089 (v0.84.0) rerank failure observation -- the + # RerankFailedEvent ERROR-level Langfuse rendering; blocked on + # the same unimplemented rerank capability. + "138-langfuse-rerank-failure-observation", ) }, + # ---- v0.16.0 spec-pin bump (v0.70.1 -> v0.84.0): new fixtures for + # proposals deferred to their own later PRs of this release. ---- + # Proposal 0062 (LLM completion streaming, spec v0.71.0). The stream + # flag on complete() + the per-chunk LlmTokenEvent + §6 streaming + # assembly are unimplemented in python until a later v0.16.0 PR; the + # observer-side atomicity + token-event fixtures defer with it. + **{ + fixture_id: "LLM completion streaming (proposal 0062) not-yet implemented" + for fixture_id in ( + "111-llm-token-event-dispatch-on-stream", + "112-llm-token-event-absent-without-stream", + "113-streamed-tool-call-reassembles-no-token-events", + "114-llm-token-event-then-failure-mid-stream", + "115-llm-token-event-call-id-links-to-completion", + "116-llm-token-event-call-level-retry-one-call-id", + "117-otel-langfuse-atomic-under-stream", + "118-llm-token-event-reasoning-delta-kind", + ) + }, + # Proposal 0075 (parallel-branches lightweight branches) IS implemented + # (v0.15.0), but fixture 119 -- the v0.73.1 callable-branch dispatch-span + # attempt_index-under-node-retry coverage round-out -- mixes a graph-style + # shape the cross-capability parser doesn't model (cf. 110) and has no + # dedicated runner here yet; defer until the harness wires it. + "119-otel-callable-branch-attempt-index-under-node-retry": ( + "proposal 0075 callable-branch attempt_index coverage round-out; harness runner not yet wired" + ), + # Proposal 0082 (structured-output failure diagnostics, spec v0.77.0). + # The LlmFailedEvent response-side surface (output_content / finish_reason + # / usage on structured_output_invalid) is unimplemented until a later + # v0.16.0 PR; the OTel / Langfuse / metrics rendering fixtures defer too. + **{ + fixture_id: "structured-output failure diagnostics (proposal 0082) not-yet implemented" + for fixture_id in ( + "120-llm-failure-event-structured-output-truncation", + "121-llm-failure-event-structured-output-schema-mismatch", + "122-llm-failure-event-response-side-null-on-non-body-failure", + "123-langfuse-failed-generation-renders-output-usage-finish-reason", + "124-otel-error-span-renders-output-usage-finish-reason", + "125-metrics-token-usage-on-structured-output-failure", + ) + }, + # Proposal 0083 (per-prompt token-budget observability, spec v0.78.0). + # The Prompt.token_budget advisory + budget-exceeded span attribute / + # WARNING / metrics are unimplemented until a later v0.16.0 PR. + **{ + fixture_id: "per-prompt token-budget observability (proposal 0083) not-yet implemented" + for fixture_id in ( + "126-token-budget-input-exceeded", + "127-token-budget-total-exceeded", + "128-token-budget-under-budget-no-warning", + "129-token-budget-absent-unchanged", + "130-langfuse-token-budget-warning-level", + "131-token-budget-on-structured-output-failure", + ) + }, + # Proposal 0084 (nested-fan-out span lineage, spec v0.81.0). The + # fan_out_index_chain / branch_name_chain event surface + the lineage- + # keyed OTel parent resolution are unimplemented until a later v0.16.0 + # PR; the no-dropped-spans keying, orphan fallback, and Langfuse parent- + # resolution fixtures defer with it. + **{ + fixture_id: "nested-fan-out span lineage (proposal 0084) not-yet implemented" + for fixture_id in ( + "132-otel-nested-fan-out-span-keying-and-llm-exact-match", + "133-otel-nested-fan-out-orphan-llm-fallback", + "134-langfuse-nested-fan-out-parent-resolution", + ) + }, + # Proposal 0087 (within-node directive execution order, spec v0.82.0). + # The conformance-adapter document-order directive rule is unimplemented + # until a later v0.16.0 PR. + "135-within-node-directive-execution-order": ( + "within-node directive execution order (proposal 0087) not-yet implemented" + ), + # Proposal 0088 (Langfuse parallel-branches mapping parity, spec + # v0.83.0). The §8.4.8 per-branch dispatch-span Langfuse observation is + # unimplemented until a later v0.16.0 PR. + "136-langfuse-parallel-branches-dispatch-span": ( + "Langfuse parallel-branches dispatch-span parity (proposal 0088) not-yet implemented" + ), } @@ -2452,6 +2541,15 @@ def _has_exception_event(span: Any) -> bool: async def _run_fixture_008_case(case: Mapping[str, Any]) -> None: case_name = case["name"] + # v0.73.1 added the detached fan-out INSTANCE error-status case + # (proposal 0061 §4.2, the fan-out-instance counterpart to the + # subgraph case). Proposal 0061 IS implemented (the instance raise + # propagates exactly like the subgraph raise), but this runner does + # not yet wire the fan-out-instance raise path (expect_raise + the + # both-spans error-status assertions); defer the single case until the + # harness wires it, keeping the other 008 cases running. + if case_name == "detached_fan_out_instance_raises_error_status_on_both_spans": + return expect_raise = case_name == "detached_subgraph_raises_error_status_on_both_spans" spans = await _run_detached_case_graph(case, expect_raise=expect_raise) diff --git a/tests/conformance/test_prompt_management.py b/tests/conformance/test_prompt_management.py index bb91f6b..691b4fa 100644 --- a/tests/conformance/test_prompt_management.py +++ b/tests/conformance/test_prompt_management.py @@ -605,6 +605,18 @@ def _message_to_dict_for_compare(message: Message) -> dict[str, Any]: "032-cross-variable-substring-stability": ( "Proposal 0047 wire-byte stability (expected_shared_prefix directive); queued for v0.13.0" ), + # ----- v0.16.0 spec-pin bump (v0.70.1 -> v0.84.0) ------------------- + # Proposal 0080 (PromptGroup arity enforcement, spec v0.75.0) -- fixture + # 035 uses a cases-only shape (no backends) the PM fixture model doesn't + # accept, and asserts the construct-time prompt_group_invalid raise that + # python does not yet implement. Defers until a later v0.16.0 PR. + "035-prompt-group-arity-rejection": ("Proposal 0080 PromptGroup arity enforcement; not implemented"), + # Proposal 0086 (PromptManager default cache_ttl_seconds, spec v0.79.0) + # -- fixture 036 uses the manager default-cache-ttl construction slot + # python does not yet implement. Defers until a later v0.16.0 PR. + "036-prompt-manager-default-cache-ttl": ( + "Proposal 0086 PromptManager default cache_ttl_seconds; not implemented" + ), } diff --git a/tests/conformance/test_retrieval_provider.py b/tests/conformance/test_retrieval_provider.py index d236955..b1090fd 100644 --- a/tests/conformance/test_retrieval_provider.py +++ b/tests/conformance/test_retrieval_provider.py @@ -39,10 +39,36 @@ # Rerank fixtures (006-012) ride the RerankProvider, which lands with # proposal 0060. Deferred so a green run means "what we implement passes." +# +# The v0.84.0 pin also pulls in the retrieval-provider WIRE-MAPPING fixtures +# (013-027) for proposals 0077 (TEI) / 0078 (Jina) / 0079 (OpenAI-compatible), +# none of which python has shipped. This runner drives only the bundled +# OpenAIEmbeddingProvider's embed() and asserts the response, so it cannot +# exercise their request-side mappings (TEI server-side prompt_name, Jina's +# task, the client-side query/document prefix) -- the contracts those +# fixtures exist to verify. Defer the whole batch until each wire-mapping +# impl PR (v0.16.0+). _DEFERRED_FIXTURES: dict[str, str] = { - p.stem: "RerankProvider not implemented (proposal 0060 not-yet)" - for p in CONFORMANCE_DIR.glob("[0-9][0-9][0-9]-*.yaml") - if 6 <= int(p.stem[:3]) <= 12 + **{ + p.stem: "RerankProvider not implemented (proposal 0060 not-yet)" + for p in CONFORMANCE_DIR.glob("[0-9][0-9][0-9]-*.yaml") + if 6 <= int(p.stem[:3]) <= 12 + }, + **{ + p.stem: "TEI wire mapping (proposal 0077) not implemented" + for p in CONFORMANCE_DIR.glob("[0-9][0-9][0-9]-*.yaml") + if 13 <= int(p.stem[:3]) <= 17 + }, + **{ + p.stem: "Jina wire mapping (proposal 0078) not implemented" + for p in CONFORMANCE_DIR.glob("[0-9][0-9][0-9]-*.yaml") + if 18 <= int(p.stem[:3]) <= 22 + }, + **{ + p.stem: "OpenAI-compatible embeddings wire mapping (proposal 0079) not implemented" + for p in CONFORMANCE_DIR.glob("[0-9][0-9][0-9]-*.yaml") + if 23 <= int(p.stem[:3]) <= 27 + }, } diff --git a/tests/test_smoke.py b/tests/test_smoke.py index 775fa61..e673231 100644 --- a/tests/test_smoke.py +++ b/tests/test_smoke.py @@ -9,7 +9,7 @@ def test_package_versions() -> None: assert openarmature.__version__ == "0.15.0" - assert openarmature.__spec_version__ == "0.70.1" + assert openarmature.__spec_version__ == "0.84.0" def test_spec_version_matches_pyproject() -> None: From f731bef7db8953c102f80319863c065081922f58 Mon Sep 17 00:00:00 2001 From: chris-colinsky Date: Mon, 29 Jun 2026 10:59:10 -0700 Subject: [PATCH 2/3] Wire 008 fan-out-instance error-status case Per a PR #197 review: _run_fixture_008_case silently skipped the detached fan-out-instance error-status case via a bare return, so the run reported it as passing without exercising it. Proposal 0061 is implemented, so this was a harness-wiring gap, not a not-yet deferral. Set expect_raise for the case and assert ERROR status + the exception event + the error category on both the parent fan-out node span and the raising instance's detached invocation span, mirroring the detached-subgraph error-status case. The case passes. --- tests/conformance/test_observability.py | 57 ++++++++++++++++++++----- 1 file changed, 47 insertions(+), 10 deletions(-) diff --git a/tests/conformance/test_observability.py b/tests/conformance/test_observability.py index cc7211c..9d15dd1 100644 --- a/tests/conformance/test_observability.py +++ b/tests/conformance/test_observability.py @@ -2541,16 +2541,10 @@ def _has_exception_event(span: Any) -> bool: async def _run_fixture_008_case(case: Mapping[str, Any]) -> None: case_name = case["name"] - # v0.73.1 added the detached fan-out INSTANCE error-status case - # (proposal 0061 §4.2, the fan-out-instance counterpart to the - # subgraph case). Proposal 0061 IS implemented (the instance raise - # propagates exactly like the subgraph raise), but this runner does - # not yet wire the fan-out-instance raise path (expect_raise + the - # both-spans error-status assertions); defer the single case until the - # harness wires it, keeping the other 008 cases running. - if case_name == "detached_fan_out_instance_raises_error_status_on_both_spans": - return - expect_raise = case_name == "detached_subgraph_raises_error_status_on_both_spans" + expect_raise = case_name in ( + "detached_subgraph_raises_error_status_on_both_spans", + "detached_fan_out_instance_raises_error_status_on_both_spans", + ) spans = await _run_detached_case_graph(case, expect_raise=expect_raise) if case_name == "detached_subgraph_two_traces_one_link": @@ -2683,6 +2677,49 @@ async def _run_fixture_008_case(case: Mapping[str, Any]) -> None: ) return + if case_name == "detached_fan_out_instance_raises_error_status_on_both_spans": + # Proposal 0061 §4.2, fan-out-instance variant: a raising detached + # fan-out instance surfaces ERROR on BOTH the parent's fan-out node + # span (parent trace, carrying the Link) and the per-instance detached + # invocation span (its own trace), each with the §4 category + an OTel + # exception event, sharing the parent invocation_id. The single-element + # fan-out (items [1]) means exactly one instance runs and raises. + fan_out_node_spans = [s for s in spans if s.name == "per_document_scoring"] + parent_fan_out = next((s for s in fan_out_node_spans if s.links), None) + assert parent_fan_out is not None, "expected a fan-out node span with a Link in the parent trace" + assert parent_fan_out.status.status_code.name == "ERROR", ( + "parent fan-out node span MUST carry ERROR for a raising detached instance" + ) + assert _has_exception_event(parent_fan_out), "parent fan-out node span MUST record the exception" + assert dict(parent_fan_out.attributes or {}).get("openarmature.error.category") == "node_exception" + assert len(parent_fan_out.links) == 1, ( + f"fan-out node span MUST carry exactly one Link; got {len(parent_fan_out.links)}" + ) + parent_trace_id = cast("Any", parent_fan_out.context).trace_id + detached_trace_id = parent_fan_out.links[0].context.trace_id + assert detached_trace_id != parent_trace_id, "detached instance + parent traces MUST be distinct" + inv_spans = [s for s in spans if s.name == "openarmature.invocation"] + detached_inv = next( + (s for s in inv_spans if cast("Any", s.context).trace_id == detached_trace_id), None + ) + parent_inv = next((s for s in inv_spans if cast("Any", s.context).trace_id == parent_trace_id), None) + assert detached_inv is not None, ( + "detached instance trace MUST root in an openarmature.invocation span" + ) + assert parent_inv is not None + assert detached_inv.status.status_code.name == "ERROR", ( + "detached instance invocation span MUST carry the instance's ERROR status (§4.2)" + ) + assert _has_exception_event(detached_inv), ( + "detached instance invocation span MUST record the exception" + ) + assert dict(detached_inv.attributes or {}).get("openarmature.error.category") == "node_exception" + parent_iid = _invocation_id_of(parent_inv) + assert parent_iid is not None and _invocation_id_of(detached_inv) == parent_iid, ( + "detached instance invocation span MUST share the parent's invocation_id" + ) + return + raise AssertionError(f"unknown sub-case {case_name!r}") From 2ca14a5689eda036fff22a060b64d6f8f1f8bfb3 Mon Sep 17 00:00:00 2001 From: chris-colinsky Date: Mon, 29 Jun 2026 11:33:16 -0700 Subject: [PATCH 3/3] Tighten v0.84.0 deferral accuracy + dedup 008 From a PR #197 review: several deferral reasons and conformance.toml statuses overstated non-implementation for partially-shipped work. - conformance.toml: 0081 not-yet -> textual-only (the non-empty matcher is enforced by the adapter; precedent 0055/0071); 0085 not-yet -> partial (the save-side enclosing_fan_out_lineage keying shipped in #194; only the resume consume-side is unshipped), and drop the false "fixture 076 defers with it" claim (076 is number-gated, not deferred); drop the stale "a new case on 008" from the 0084 comment (that case is 0061's and is now wired). - Reword the 132 / 023-027 / 119 / 022-023 deferral reasons to state what actually ships (the 0084 lineage keying, the 0059 embed wire, 0075 callable branches, the structured_output_invalid base mapping) versus what is genuinely deferred. - Extract _assert_detached_raise_both_spans from the two near-identical 008 raise-case blocks, and add the parent openarmature.invocation ERROR assertion the fixtures' span_trees expect. --- conformance.toml | 19 ++- tests/conformance/test_llm_provider.py | 10 +- tests/conformance/test_observability.py | 150 +++++++++---------- tests/conformance/test_retrieval_provider.py | 7 +- 4 files changed, 98 insertions(+), 88 deletions(-) diff --git a/conformance.toml b/conformance.toml index 73d927c..7db595a 100644 --- a/conformance.toml +++ b/conformance.toml @@ -801,9 +801,14 @@ status = "not-yet" # Spec v0.76.0 (proposal 0081). Conformance-adapter value-matcher # vocabulary (§5.10) -- ratifies the fixture matcher tokens already in -# use. Not-yet (no fixture changes); awaits a conformance-pin batch. +# use. Descriptive (no fixture changes); the one tightening with teeth +# ( = non-empty) is already enforced by the adapter +# (tests/conformance/test_observability.py _value_matches rejects the +# empty string). No module-level change required; matches the +# conformance-adapter textual-only precedent (0055 / 0071). [proposals."0081"] -status = "not-yet" +status = "textual-only" +since = "0.16.0" # Spec v0.77.0 (proposal 0082). Structured-output failure diagnostics # (graph-engine §6 -- LlmFailedEvent response-side surface for @@ -822,16 +827,16 @@ status = "not-yet" # Spec v0.81.0 (proposal 0084). Nested-fan-out span lineage chain # (graph-engine §6 fan_out_index_chain / branch_name_chain + # observability §4 / §5.5 lineage-resolved parent). Not-yet; graph- -# engine fixture 039 and observability fixtures 132-134 (+ a new case -# on 008) defer with it. +# engine fixture 039 and observability fixtures 132-134 defer with it. [proposals."0084"] status = "not-yet" # Spec v0.80.0 (proposal 0085). Nested-fan-out checkpoint resume -# lineage (pipeline-utilities §10.11 enclosing_fan_out_lineage). Not- -# yet; pipeline-utilities fixture 076 defers with it. +# lineage (pipeline-utilities §10.11 enclosing_fan_out_lineage). [proposals."0085"] -status = "not-yet" +status = "partial" +since = "0.16.0" +note = "The SAVE-side enclosing_fan_out_lineage keying (pipeline-utilities §10.11) shipped in #194: a fan-out instance's checkpoint tracking key carries the enclosing fan-out instance lineage in the in-memory dict and through the checkpoint projection / lookup / cleanup / restore, so concurrent outer instances no longer collide. partial because the RESUME consume-side is not yet shipped: a fan-out nested inside an outer instance re-runs rather than skipping on resume, since the saved record format carries no lineage (tracked as a follow-up). pipeline-utilities fixture 076 is not collected by the test_pipeline_utilities.py _LAST_DRIVEN_FIXTURE number gate (it is not deferred); the resume consume-side plus its fixture wiring land in a later PR." # Spec v0.79.0 (proposal 0086). Service-wide default cache_ttl_seconds # on PromptManager (prompt-management §6). Not-yet; prompt-management diff --git a/tests/conformance/test_llm_provider.py b/tests/conformance/test_llm_provider.py index 3d2dc62..3116742 100644 --- a/tests/conformance/test_llm_provider.py +++ b/tests/conformance/test_llm_provider.py @@ -115,9 +115,13 @@ # ----- v0.16.0 spec-pin bump (v0.70.1 -> v0.84.0) ------------------- # Proposal 0082 (structured-output failure diagnostics, spec v0.77.0) # extended fixtures 022/023 to additionally assert the now-required - # finish_reason + usage on the structured_output_invalid error; python - # does not yet carry those response-side fields on the error, so the - # extended fixtures defer until a later v0.16.0 PR lands 0082. + # finish_reason + usage on the structured_output_invalid error. The + # base structured_output_invalid mapping still has coverage in + # tests/unit/test_structured_output.py:: + # test_pydantic_validation_failure_wraps_in_structured_output_invalid; + # only 0082's additive finish_reason / usage-on-error response-side + # fields are unimplemented, which is why the conformance fixtures defer + # until a later v0.16.0 PR lands 0082. "022-structured-output-parse-failure": ( "Proposal 0082 finish_reason/usage on structured_output_invalid; not implemented" ), diff --git a/tests/conformance/test_observability.py b/tests/conformance/test_observability.py index 9d15dd1..bafbee0 100644 --- a/tests/conformance/test_observability.py +++ b/tests/conformance/test_observability.py @@ -316,7 +316,8 @@ def _reset_otel_global_tracer_provider(restore_to: object) -> None: # shape the cross-capability parser doesn't model (cf. 110) and has no # dedicated runner here yet; defer until the harness wires it. "119-otel-callable-branch-attempt-index-under-node-retry": ( - "proposal 0075 callable-branch attempt_index coverage round-out; harness runner not yet wired" + "0075 shipped v0.15.0; runner for this callable-branch-attempt-index-under-retry " + "case not yet wired -- harness gap, not unimplemented" ), # Proposal 0082 (structured-output failure diagnostics, spec v0.77.0). # The LlmFailedEvent response-side surface (output_content / finish_reason @@ -347,15 +348,24 @@ def _reset_otel_global_tracer_provider(restore_to: object) -> None: "131-token-budget-on-structured-output-failure", ) }, - # Proposal 0084 (nested-fan-out span lineage, spec v0.81.0). The - # fan_out_index_chain / branch_name_chain event surface + the lineage- - # keyed OTel parent resolution are unimplemented until a later v0.16.0 - # PR; the no-dropped-spans keying, orphan fallback, and Langfuse parent- - # resolution fixtures defer with it. + # Proposal 0084 (nested-fan-out span lineage, spec v0.81.0). + # Fixture 132's nested-lineage dispatch KEYING shipped in #194 (the + # OTel + Langfuse observers key dispatches by the full enclosing + # fan-out / branch lineage, so no spans drop). It stays deferred for + # 0084's remaining surface: the fan_out_index_chain / branch_name_chain + # arrive on the provider events with 0084 (see graph/events.py), and the + # nested-orphan LLM parent resolution is top-level-instance-only as of + # #195 -- the at-any-depth generalization rides 0084. + "132-otel-nested-fan-out-span-keying-and-llm-exact-match": ( + "nested-lineage span keying shipped (#194); deferred for 0084's event-chain " + "surface (fan_out_index_chain / branch_name_chain on provider events) and the " + "nested-orphan LLM parent (top-level only as of #195)" + ), + # Proposal 0084: 133 (orphan LLM fallback) + 134 (Langfuse parent + # resolution) defer with 0084's lineage-resolved parent surface. **{ fixture_id: "nested-fan-out span lineage (proposal 0084) not-yet implemented" for fixture_id in ( - "132-otel-nested-fan-out-span-keying-and-llm-exact-match", "133-otel-nested-fan-out-orphan-llm-fallback", "134-langfuse-nested-fan-out-parent-resolution", ) @@ -2539,6 +2549,54 @@ def _has_exception_event(span: Any) -> bool: return any(getattr(e, "name", None) == "exception" for e in events) +def _assert_detached_raise_both_spans( + spans: Sequence[ReadableSpan], + *, + parent_span_name: str, + expected_link_count: int, +) -> None: + # Proposal 0061 §4.2: a raising detached unit surfaces ERROR on BOTH the + # parent's dispatch span (the one carrying the Link, named + # ``parent_span_name``) and the per-unit detached invocation span (its own + # trace) -- distinct traces, shared invocation_id, each with the §4 category + # + an OTel exception event. The parent's own openarmature.invocation span + # also inherits ERROR (§4.4): the fixtures' expected span_trees show the + # parent invocation ERROR. + parent_dispatch = next((s for s in spans if s.name == parent_span_name and s.links), None) + assert parent_dispatch is not None, f"expected a parent {parent_span_name!r} span carrying a Link" + assert parent_dispatch.status.status_code.name == "ERROR", ( + f"parent {parent_span_name!r} span MUST carry ERROR for a raising detached unit" + ) + assert _has_exception_event(parent_dispatch), ( + f"parent {parent_span_name!r} span MUST record the exception" + ) + assert dict(parent_dispatch.attributes or {}).get("openarmature.error.category") == "node_exception" + assert len(parent_dispatch.links) == expected_link_count, ( + f"parent {parent_span_name!r} span MUST carry exactly {expected_link_count} Link(s); " + f"got {len(parent_dispatch.links)}" + ) + parent_trace_id = cast("Any", parent_dispatch.context).trace_id + detached_trace_id = parent_dispatch.links[0].context.trace_id + assert detached_trace_id != parent_trace_id, "detached + parent traces MUST be distinct" + inv_spans = [s for s in spans if s.name == "openarmature.invocation"] + detached_inv = next((s for s in inv_spans if cast("Any", s.context).trace_id == detached_trace_id), None) + parent_inv = next((s for s in inv_spans if cast("Any", s.context).trace_id == parent_trace_id), None) + assert detached_inv is not None, "detached trace MUST root in an openarmature.invocation span" + assert parent_inv is not None + assert parent_inv.status.status_code.name == "ERROR", ( + "parent openarmature.invocation span MUST inherit ERROR for a raising detached unit (§4.4)" + ) + assert detached_inv.status.status_code.name == "ERROR", ( + "detached invocation span MUST carry the detached unit's ERROR status (§4.2)" + ) + assert _has_exception_event(detached_inv), "detached invocation span MUST record the exception" + assert dict(detached_inv.attributes or {}).get("openarmature.error.category") == "node_exception" + parent_iid = _invocation_id_of(parent_inv) + assert parent_iid is not None and _invocation_id_of(detached_inv) == parent_iid, ( + "detached invocation span MUST share the parent's invocation_id" + ) + + async def _run_fixture_008_case(case: Mapping[str, Any]) -> None: case_name = case["name"] expect_raise = case_name in ( @@ -2644,79 +2702,17 @@ async def _run_fixture_008_case(case: Mapping[str, Any]) -> None: return if case_name == "detached_subgraph_raises_error_status_on_both_spans": - # Proposal 0061 §4.2: a raising detached subgraph surfaces ERROR - # on BOTH the parent's dispatch span and the detached invocation - # span — distinct traces, shared invocation_id, each with the §4 - # category + an OTel exception event. - dispatch_spans = [s for s in spans if s.name == "dispatch"] - parent_dispatch = next((s for s in dispatch_spans if s.links), None) - assert parent_dispatch is not None, "expected a parent 'dispatch' span with a Link" - assert parent_dispatch.status.status_code.name == "ERROR", ( - "parent dispatch span MUST carry ERROR for a raising detached subgraph" - ) - assert _has_exception_event(parent_dispatch), "parent dispatch span MUST record the exception" - assert dict(parent_dispatch.attributes or {}).get("openarmature.error.category") == "node_exception" - parent_trace_id = cast("Any", parent_dispatch.context).trace_id - detached_trace_id = parent_dispatch.links[0].context.trace_id - assert detached_trace_id != parent_trace_id, "detached + parent traces MUST be distinct" - inv_spans = [s for s in spans if s.name == "openarmature.invocation"] - detached_inv = next( - (s for s in inv_spans if cast("Any", s.context).trace_id == detached_trace_id), None - ) - parent_inv = next((s for s in inv_spans if cast("Any", s.context).trace_id == parent_trace_id), None) - assert detached_inv is not None, "detached trace MUST root in an openarmature.invocation span" - assert parent_inv is not None - assert detached_inv.status.status_code.name == "ERROR", ( - "detached invocation span MUST carry the detached unit's ERROR status (§4.2)" - ) - assert _has_exception_event(detached_inv), "detached invocation span MUST record the exception" - assert dict(detached_inv.attributes or {}).get("openarmature.error.category") == "node_exception" - parent_iid = _invocation_id_of(parent_inv) - assert parent_iid is not None and _invocation_id_of(detached_inv) == parent_iid, ( - "detached invocation span MUST share the parent's invocation_id" - ) + # The parent's subgraph-dispatch span (named "dispatch") carries the + # single Link to the one detached subgraph trace. + _assert_detached_raise_both_spans(spans, parent_span_name="dispatch", expected_link_count=1) return if case_name == "detached_fan_out_instance_raises_error_status_on_both_spans": - # Proposal 0061 §4.2, fan-out-instance variant: a raising detached - # fan-out instance surfaces ERROR on BOTH the parent's fan-out node - # span (parent trace, carrying the Link) and the per-instance detached - # invocation span (its own trace), each with the §4 category + an OTel - # exception event, sharing the parent invocation_id. The single-element - # fan-out (items [1]) means exactly one instance runs and raises. - fan_out_node_spans = [s for s in spans if s.name == "per_document_scoring"] - parent_fan_out = next((s for s in fan_out_node_spans if s.links), None) - assert parent_fan_out is not None, "expected a fan-out node span with a Link in the parent trace" - assert parent_fan_out.status.status_code.name == "ERROR", ( - "parent fan-out node span MUST carry ERROR for a raising detached instance" - ) - assert _has_exception_event(parent_fan_out), "parent fan-out node span MUST record the exception" - assert dict(parent_fan_out.attributes or {}).get("openarmature.error.category") == "node_exception" - assert len(parent_fan_out.links) == 1, ( - f"fan-out node span MUST carry exactly one Link; got {len(parent_fan_out.links)}" - ) - parent_trace_id = cast("Any", parent_fan_out.context).trace_id - detached_trace_id = parent_fan_out.links[0].context.trace_id - assert detached_trace_id != parent_trace_id, "detached instance + parent traces MUST be distinct" - inv_spans = [s for s in spans if s.name == "openarmature.invocation"] - detached_inv = next( - (s for s in inv_spans if cast("Any", s.context).trace_id == detached_trace_id), None - ) - parent_inv = next((s for s in inv_spans if cast("Any", s.context).trace_id == parent_trace_id), None) - assert detached_inv is not None, ( - "detached instance trace MUST root in an openarmature.invocation span" - ) - assert parent_inv is not None - assert detached_inv.status.status_code.name == "ERROR", ( - "detached instance invocation span MUST carry the instance's ERROR status (§4.2)" - ) - assert _has_exception_event(detached_inv), ( - "detached instance invocation span MUST record the exception" - ) - assert dict(detached_inv.attributes or {}).get("openarmature.error.category") == "node_exception" - parent_iid = _invocation_id_of(parent_inv) - assert parent_iid is not None and _invocation_id_of(detached_inv) == parent_iid, ( - "detached instance invocation span MUST share the parent's invocation_id" + # Fan-out-instance variant: the parent's fan-out node span + # ("per_document_scoring") carries the Link. The single-element fan-out + # (items [1]) means exactly one instance runs and raises -> one Link. + _assert_detached_raise_both_spans( + spans, parent_span_name="per_document_scoring", expected_link_count=1 ) return diff --git a/tests/conformance/test_retrieval_provider.py b/tests/conformance/test_retrieval_provider.py index b1090fd..85eb943 100644 --- a/tests/conformance/test_retrieval_provider.py +++ b/tests/conformance/test_retrieval_provider.py @@ -65,7 +65,12 @@ if 18 <= int(p.stem[:3]) <= 22 }, **{ - p.stem: "OpenAI-compatible embeddings wire mapping (proposal 0079) not implemented" + p.stem: ( + "OpenAI-compatible embed wire ships via the bundled OpenAIEmbeddingProvider " + "(proposal 0059); deferred because the harness lacks a wire-capture primitive " + "(expected_wire_request / url / headers) and 0079's dimensions / input_type " + "request knobs are unimplemented" + ) for p in CONFORMANCE_DIR.glob("[0-9][0-9][0-9]-*.yaml") if 23 <= int(p.stem[:3]) <= 27 },