From a57e8bd0bc4a00a590f92e7e691940a3eb32ffa1 Mon Sep 17 00:00:00 2001 From: chris-colinsky Date: Sun, 14 Jun 2026 20:37:37 -0700 Subject: [PATCH] Pin spec v0.55.1 and adopt proposal 0065 Advance the pinned spec submodule from v0.54.0 to v0.55.1, adopting proposal 0065 (failure-isolation cause fidelity at non-node placements). The 0065 runtime behavior already shipped earlier in the cycle; this marks it implemented in conformance.toml and wires its conformance fixture 064, which passes all three cases (instance, branch, and uncategorized-cause sites) against the merged engine fix. Two test-harness gaps that fixture 064 is the first to hit are closed: the cases-shape runner now threads a shared plural subgraphs block into each case, and the CasesFixture parser model accepts subgraphs at the top level. The pin is v0.55.1 rather than v0.55.0 because v0.55.1 adds an observability span-links text reconciliation with no python-observable change. Sync the three spec-version declarations, regenerate AGENTS.md, and add the consolidated pin-journey changelog note. --- CHANGELOG.md | 5 ++-- conformance.toml | 18 +++++++++++- openarmature-spec | 2 +- pyproject.toml | 2 +- src/openarmature/AGENTS.md | 4 +-- src/openarmature/__init__.py | 2 +- tests/conformance/harness/fixtures.py | 16 +++++++---- tests/conformance/test_pipeline_utilities.py | 30 ++++++++++++-------- tests/test_smoke.py | 2 +- 9 files changed, 54 insertions(+), 27 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3460ee2..9b4ac6a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,8 +14,9 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). The ### Changed - **`RetryMiddleware` now takes a `RetryConfig` record** instead of individual constructor kwargs (proposal 0050 prep). The four retry settings (`max_attempts` / `classifier` / `backoff` / `on_retry`, each optional) move onto a frozen `RetryConfig`; construct as `RetryMiddleware(RetryConfig(max_attempts=...))`, while bare `RetryMiddleware()` still applies the defaults. This is a breaking change to the `RetryMiddleware` constructor. The record is the same shape the upcoming call-level `complete(retry=...)` parameter will accept, so one retry config serves both the per-node and per-call layers. `None` fields resolve to the canonical defaults (`default_classifier` / `exponential_jitter_backoff`) at use, preserving the prior behavior. -- **Failure-isolation events report the originating cause's category at non-node placements** (proposal 0065, pipeline-utilities §6.3). When `FailureIsolationMiddleware` runs as instance middleware (§9.7), branch middleware (§11.7), or parent-node middleware on a fan-out / parallel-branches node, the graph engine has already wrapped the originating error as a `node_exception` carrier before the middleware catches it. `FailureIsolatedEvent.caught_exception.category` now resolves through that carrier (and any nested carriers) to the nearest categorized originating cause and reports its category instead of the masking `node_exception`, so the reported category agrees with what the §6.1 retry classifier acted on. For example, an instance whose retries exhaust on `provider_unavailable` now surfaces `provider_unavailable` rather than `node_exception`. The `message` tracks the resolved cause for category/message coherence. Node-level placement was already faithful and is unchanged, and catch/degrade behavior is unchanged at every site (only the event's reported cause changes). The wrapped-instance/branch lineage SHOULD (`fan_out_index` / `branch_name`) is deferred to a follow-up, since it needs the engine to surface per-instance identity to the wrapping-site middleware. -- **Observer privacy flag `disable_llm_payload` renamed to `disable_provider_payload`** (proposal 0059, observability §5.5.4, spec v0.54.0). The observer-level flag on both bundled observers (`OTelObserver` and `LangfuseObserver`) is renamed, and its scope broadens from LLM-completion payload to any provider-call payload (LLM completion today; embedding and rerank when those land). This is a breaking change to both observer constructors: config passing `disable_llm_payload=True` (or `False`) updates to `disable_provider_payload=...` with no other change. The default stays `True` (payload suppressed), and the gating behavior for `LlmCompletionEvent` / `LlmFailedEvent` rendering is unchanged at every existing site. The rename is the only part of proposal 0059 adopted this cycle: the retrieval-provider capability itself (the `EmbeddingProvider` protocol, the `EmbeddingEvent` / `EmbeddingFailedEvent` typed variants, and the embedding span / observation mapping) is not yet implemented and rides as `not-yet` in `conformance.toml`. The §5.5.4 rename touches existing LLM-payload gating, so it lands with the pin. Pinned spec advances v0.53.0 → v0.54.0. +- **Failure-isolation events report the originating cause's category at non-node placements** (proposal 0065, pipeline-utilities §6.3). When `FailureIsolationMiddleware` runs as instance middleware (§9.7), branch middleware (§11.7), or parent-node middleware on a fan-out / parallel-branches node, the graph engine has already wrapped the originating error as a `node_exception` carrier before the middleware catches it. `FailureIsolatedEvent.caught_exception.category` now resolves through that carrier (and any nested carriers) to the nearest categorized originating cause and reports its category instead of the masking `node_exception`, so the reported category agrees with what the §6.1 retry classifier acted on. For example, an instance whose retries exhaust on `provider_unavailable` now surfaces `provider_unavailable` rather than `node_exception`. The `message` tracks the resolved cause for category/message coherence. Node-level placement was already faithful and is unchanged, and catch/degrade behavior is unchanged at every site (only the event's reported cause changes). The wrapped-instance/branch lineage SHOULD (`fan_out_index` / `branch_name`) is deferred to a follow-up, since it needs the engine to surface per-instance identity to the wrapping-site middleware. `conformance.toml` marks proposal 0065 `implemented`, and conformance fixture 064 (three cases: the §9.7 instance and §11.7 branch sites plus an uncategorized cause) passes. +- **Observer privacy flag `disable_llm_payload` renamed to `disable_provider_payload`** (proposal 0059, observability §5.5.4, spec v0.54.0). The observer-level flag on both bundled observers (`OTelObserver` and `LangfuseObserver`) is renamed, and its scope broadens from LLM-completion payload to any provider-call payload (LLM completion today; embedding and rerank when those land). This is a breaking change to both observer constructors: config passing `disable_llm_payload=True` (or `False`) updates to `disable_provider_payload=...` with no other change. The default stays `True` (payload suppressed), and the gating behavior for `LlmCompletionEvent` / `LlmFailedEvent` rendering is unchanged at every existing site. The rename is the only part of proposal 0059 adopted this cycle: the retrieval-provider capability itself (the `EmbeddingProvider` protocol, the `EmbeddingEvent` / `EmbeddingFailedEvent` typed variants, and the embedding span / observation mapping) is not yet implemented and rides as `not-yet` in `conformance.toml`. The §5.5.4 rename touches existing LLM-payload gating, so it lands with the pin. +- **Pinned spec advances v0.53.0 → v0.55.1 across the v0.14.0 cycle**, in two steps: v0.54.0 (proposal 0059, the observer-flag rename above) and v0.55.1 (proposal 0065 above; the v0.55.1 patch also carries an observability §11 span-links text reconciliation that narrows an *Out of scope* bullet, with no python-observable change). `conformance.toml` records 0065 as `implemented` and 0059 as `not-yet` (only its cross-spec flag rename was adopted). ### Fixed diff --git a/conformance.toml b/conformance.toml index 35e8103..c530fa1 100644 --- a/conformance.toml +++ b/conformance.toml @@ -32,7 +32,7 @@ [manifest] implementation = "openarmature-python" -spec_pin = "v0.54.0" +spec_pin = "v0.55.1" # Status values: # implemented — shipped behavior matches the proposal's contract @@ -605,3 +605,19 @@ since = "0.13.0" # the pin even though the embedding capability does not). [proposals."0059"] status = "not-yet" + +# Spec v0.55.0 (proposal 0065; repo pins v0.55.1). Failure-isolation +# cause fidelity at non-node placements (pipeline-utilities §6.3 / +# §11.7). At instance (§9.7), branch (§11.7), and parent-node +# middleware sites the engine wraps the originating error as a §4 +# ``node_exception`` carrier before isolation catches it; +# ``FailureIsolatedEvent.caught_exception.category`` now resolves +# THROUGH that carrier to the originating cause instead of reporting the +# masking ``node_exception``. Fixture 064 (three cases) passes; +# node-level placement (fixture 061) was already faithful. The pin is +# v0.55.1 rather than v0.55.0 because v0.55.1 adds an observability §11 +# span-links text reconciliation (no proposal, no python-observable +# change). +[proposals."0065"] +status = "implemented" +since = "0.14.0" diff --git a/openarmature-spec b/openarmature-spec index c7d6d4e..deea1a6 160000 --- a/openarmature-spec +++ b/openarmature-spec @@ -1 +1 @@ -Subproject commit c7d6d4ef4d02573ef2bf15d977e23ba86d4ce584 +Subproject commit deea1a6198f643afb64c21964aa2644d8d0d93e5 diff --git a/pyproject.toml b/pyproject.toml index 30ec2e1..64225cb 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.54.0" +spec_version = "0.55.1" [dependency-groups] dev = [ diff --git a/src/openarmature/AGENTS.md b/src/openarmature/AGENTS.md index 1319888..b97e94b 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.13.0 (spec v0.54.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`.* +*This is the agent guide bundled with the openarmature Python package, version 0.13.0 (spec v0.55.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`.* ## 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.54.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._ +_Sourced from openarmature-spec v0.55.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._ ### Capability: `graph-engine` diff --git a/src/openarmature/__init__.py b/src/openarmature/__init__.py index 2f7fd3f..1f59427 100644 --- a/src/openarmature/__init__.py +++ b/src/openarmature/__init__.py @@ -25,7 +25,7 @@ """ __version__ = "0.13.0" -__spec_version__ = "0.54.0" +__spec_version__ = "0.55.1" # 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/harness/fixtures.py b/tests/conformance/harness/fixtures.py index 4ac1daa..c103266 100644 --- a/tests/conformance/harness/fixtures.py +++ b/tests/conformance/harness/fixtures.py @@ -165,17 +165,21 @@ class CasesFixture(_ForbidExtras): Used by ``007-compile-errors``, the checkpointing fixtures (024–031), and the determinism / multi-run observability fixtures. Optional shared - ``subgraph`` / ``subgraph_with_idx`` at the top level apply across all - cases. Any other top-level key not listed here is rejected. + ``subgraph`` / ``subgraph_with_idx`` / ``subgraphs`` at the top level + apply across all cases. Any other top-level key not listed here is + rejected. """ cases: list[CaseSpec] - # Shared graph-shape blocks that apply across every case. Empirically - # only `subgraph` and `subgraph_with_idx` appear at the top level of - # cases-fixtures; the plural `subgraphs` form has not been seen at - # the cases-fixture top level. + # Shared graph-shape blocks that apply across every case. The singular + # `subgraph` / `subgraph_with_idx` and the plural `subgraphs` map + # (name -> graph-spec, as the parallel-branches fixtures use) may all + # appear at the cases-fixture top level. Fixture 064 (failure-isolation + # cause fidelity) is the first to share a plural `subgraphs:` across + # cases. subgraph: SubgraphDefinition | None = None subgraph_with_idx: SubgraphDefinition | None = None + subgraphs: dict[str, SubgraphDefinition] | None = None # --------------------------------------------------------------------------- diff --git a/tests/conformance/test_pipeline_utilities.py b/tests/conformance/test_pipeline_utilities.py index 08bc3bb..59e5144 100644 --- a/tests/conformance/test_pipeline_utilities.py +++ b/tests/conformance/test_pipeline_utilities.py @@ -83,13 +83,14 @@ def _load(path: Path) -> dict[str, Any]: # the `cases:` shape carries seeded-record + migrations + resume blocks. _LAST_DRIVEN_FIXTURE = 38 -# Failure-isolation fixtures (058-063, proposal 0050 §6.3) are middleware -# fixtures this runner handles. They sit past _LAST_DRIVEN_FIXTURE only -# because the 039-057 range (state migration / checkpoint fan-out) is owned -# by dedicated runners (test_state_migration.py / test_checkpoint.py), not -# because this runner can't drive them. Fixture 064 (cause fidelity) joins -# when the spec pin advances to v0.55.0. -_FAILURE_ISOLATION_FIXTURES = frozenset(range(58, 64)) +# Failure-isolation fixtures (058-064, proposals 0050 §6.3 + 0065) are +# middleware fixtures this runner handles. They sit past _LAST_DRIVEN_FIXTURE +# only because the 039-057 range (state migration / checkpoint fan-out) is +# owned by dedicated runners (test_state_migration.py / test_checkpoint.py), +# not because this runner can't drive them. Fixture 064 (cause fidelity at +# non-node placements, proposal 0065) joined when the spec pin advanced to +# v0.55.1. +_FAILURE_ISOLATION_FIXTURES = frozenset(range(58, 65)) def _fixture_paths() -> list[Path]: @@ -488,13 +489,18 @@ async def test_pipeline_utility_fixture( pytest.skip(f"{fixture_id}: {_DEFERRED_FIXTURES[fixture_id]}") spec = _load(fixture_path) - # Cases-shape fixtures (014, 016, 018-019, 021-023): each case is - # a self-contained graph + middleware + expected block. The outer + # Cases-shape fixtures (014, 016, 018-019, 021-023, 064): each case + # is a self-contained graph + middleware + expected block. The outer # fixture may define shared ``subgraph:`` / ``subgraph_with_idx:`` - # blocks that every case references; merge them into each case - # before dispatching so the case sees them as if they were its own. + # (singular) or ``subgraphs:`` (plural, name -> graph-spec, as the + # parallel-branches fixtures use) blocks that every case references; + # merge them into each case before dispatching so the case sees them + # as if they were its own. ``setdefault`` below preserves any block a + # case defines for itself. if "cases" in spec: - shared_subgraph_blocks = {k: spec[k] for k in ("subgraph", "subgraph_with_idx") if k in spec} + shared_subgraph_blocks = { + k: spec[k] for k in ("subgraph", "subgraph_with_idx", "subgraphs") if k in spec + } for case in spec["cases"]: case_name = case.get("name", "") merged: dict[str, Any] = dict(case) diff --git a/tests/test_smoke.py b/tests/test_smoke.py index 2547202..ded6303 100644 --- a/tests/test_smoke.py +++ b/tests/test_smoke.py @@ -9,7 +9,7 @@ def test_package_versions() -> None: assert openarmature.__version__ == "0.13.0" - assert openarmature.__spec_version__ == "0.54.0" + assert openarmature.__spec_version__ == "0.55.1" def test_spec_version_matches_pyproject() -> None: