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: