From 630804ca4bfea3294f1f1f624fc99695cbb8de74 Mon Sep 17 00:00:00 2001 From: chris-colinsky Date: Fri, 5 Jun 2026 18:45:47 -0700 Subject: [PATCH 1/2] chore(release): v0.12.0-rc1 Pyproject version + __version__ + tests/test_smoke.py assertion all bumped to 0.12.0rc1 ahead of the rc tag push per RELEASING.md. AGENTS.md regenerated to pick up the new version string in its header line. uv.lock refreshed because the package's own version changed. CHANGELOG flips [Unreleased] to [0.12.0] -- 2026-06-05 with a new headline paragraph summarizing the cycle: three implemented proposals (0048 read-symmetric metadata + queryable observer docs; 0052 implementation attribution attributes on every OTel invocation span + every Langfuse Trace; 0054 per-invocation observer event drain), two textual-only acks (0051 Langfuse trace input/output caveat; 0053 shared-parent boundary clarification), one Fixed (retry middleware now resets the invocation-metadata ContextVar between attempts), plus the production-observability example growing the queryable accumulator + drain_events_for pattern end-to-end. The existing Production observability example. Added entry was written when the example first shipped in an earlier cycle; the bullet now also covers the v0.12.0 additions to that example (LlmUsageAccumulator, persist node drain + read + drop pattern, InvocationCompletedEvent backstop, OTel formatter surfacing the invocation span with its attribution attributes). After this PR merges, push the v0.12.0-rc1 tag from a freshly pulled main. The release workflow routes -rc tags to TestPyPI; verify the rc per RELEASING.md before preparing the real-release bump in a separate PR. --- CHANGELOG.md | 6 ++++-- pyproject.toml | 2 +- src/openarmature/AGENTS.md | 2 +- src/openarmature/__init__.py | 2 +- tests/test_smoke.py | 2 +- uv.lock | 2 +- 6 files changed, 9 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d850fc78..41c2ac7b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,9 @@ All notable changes to `openarmature-python` are documented in this file. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). The package follows [Semantic Versioning](https://semver.org/); pre-1.0 minor bumps may carry behavioral changes per [spec governance](https://github.com/LunarCommand/openarmature-spec/blob/main/GOVERNANCE.md). -## [Unreleased] +## [0.12.0] — 2026-06-05 + +Observability release. The pinned spec advances from v0.38.0 to v0.46.0, absorbing eight accepted proposals (0047-0054). Three ship as fully implemented this cycle: proposal 0048 grows a read-symmetric `get_invocation_metadata()` API + a §9 *Queryable observer pattern* concept doc section; proposal 0052 puts `openarmature.implementation.name` + `.version` attribution attributes on every OTel invocation span + every Langfuse Trace; proposal 0054 ships `CompiledGraph.drain_events_for(invocation_id, *, timeout)` as the architectural pair to 0048's §9.4 accumulator lifecycle. Two ship as textual-only acks (0051 Langfuse trace I/O caveat; 0053 §3.4 shared-parent boundary clarification). One Fixed: the retry middleware now resets the invocation-metadata ContextVar between attempts per §3.4. The production-observability example grows the queryable accumulator + drain_events_for pattern end-to-end so the new APIs have a runnable demo. ### Changed @@ -20,7 +22,7 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). The - **`CompiledGraph.drain_events_for(invocation_id, *, timeout=5.0) -> DrainSummary`** (proposal 0054, spec graph-engine §6 *Per-invocation drain*, v0.46.0). The architectural pair to proposal 0048's §9.4 queryable observer accumulator lifecycle: a terminal node calling `await graph.drain_events_for(state.invocation_id)` blocks until every event dispatched for that invocation has reached every attached observer, typically followed by a read against a queryable observer accumulator whose bucket the drain has now caught up to. Snapshot semantic: the drain awaits the events dispatched as of call time; new emissions after the call are out of scope. Reuses the existing `DrainSummary` shape verbatim — no new `InvocationDrainSummary` variant. **Load-bearing divergence from `drain()`**: a per-invocation drain timeout MUST NOT cancel the delivery worker, in contrast to `drain()`'s shutdown semantics. The graph stays serving other invocations after the timeout fires; the deliver loop keeps processing the queue. Default timeout is `5.0` seconds; `None` waits indefinitely; `0.0` is a non-blocking check. Negative or `NaN` timeout raises `ValueError` at the API boundary. Unknown `invocation_id` (already drained or never started) returns an empty summary, not an error. - **`get_invocation_metadata()` read-symmetric API** (proposal 0048, observability §3.4, spec v0.40.0). The canonical spec-idiomatic public name for the §3.4 read access pairs with `set_invocation_metadata()` on the write side: same function object as the historical `current_invocation_metadata`, exposed for callers wishing to use the symmetric `get_/set_` naming. Returns the `MappingProxyType` snapshot of the current async context's view (caller baseline + in-node augments), or the empty mapping outside any active invocation. Read-only — callers MUST NOT mutate it. Both names are now exported from `openarmature.observability`; existing `current_invocation_metadata` callers continue to work unchanged. - **`docs/concepts/observability.md` §9 *Queryable observer pattern*** documents the convention-only observer-attached read methods that proposal 0048 §9 blesses: how to add a `get_*` read method to a custom observer (§9.1), the async-safety contract for concurrent reads under in-flight delivery (§9.2), the three-channel data-access guidance (typed State / untyped invocation metadata / queryable observer accumulator, §9.3, with a side-by-side table), and the lifecycle / explicit `drop(invocation_id)` discipline (§9.4). No new abstract surface on `Observer` per the spec — the pattern is convention-only and exists to bless the existing observer-state read shape used in production code. -- **Production observability example.** `examples/production-observability/` demonstrates the production-grade observability stack end-to-end: `OTelObserver` + `LangfuseObserver` attached to the same graph (proposal 0031), `trace_input_from_state` / `trace_output_from_state` caller hooks on the Langfuse observer (proposal 0043 §8.4.1) deriving domain dicts from State, the built-in `TimingMiddleware` recording per-node duration via an `on_complete` callback, and `invoke(metadata={...})` carrying multi-tenant identifiers (tenantId / requestId / featureFlag) that both observers pick up at once. `InMemoryLangfuseClient` + `InMemorySpanExporter` capture in-process so the demo prints what each backend would have ingested without needing real production credentials. +- **Production observability example.** `examples/production-observability/` demonstrates the production-grade observability stack end-to-end: `OTelObserver` + `LangfuseObserver` attached to the same graph (proposal 0031), `trace_input_from_state` / `trace_output_from_state` caller hooks on the Langfuse observer (proposal 0043 §8.4.1) deriving domain dicts from State, the built-in `TimingMiddleware` recording per-node duration via an `on_complete` callback, and `invoke(metadata={...})` carrying multi-tenant identifiers (tenantId / requestId / featureFlag) that both observers pick up at once. `InMemoryLangfuseClient` + `InMemorySpanExporter` capture in-process so the demo prints what each backend would have ingested without needing real production credentials. The v0.12.0 cycle extends the demo with a third observer: an `LlmUsageAccumulator` queryable-accumulator pattern subscribing to LLM-namespace events, accumulating per-invocation token totals keyed by `current_invocation_id()`, and a terminal `persist` node that calls `await graph.drain_events_for(current_invocation_id(), timeout=2.0)` to synchronize on the deliver loop before reading the bucket and dropping it. The accumulator's `__call__` handles `InvocationCompletedEvent` as a backstop so a drain timeout doesn't leak buckets. The example's OTel formatter also surfaces the root `openarmature.invocation` span with its proposal 0052 implementation attribution attributes alongside the per-node spans. - **Chat-with-multimodal example.** `examples/chat-with-multimodal/` demonstrates `ChatPrompt` + `PlaceholderSegment` (proposal 0046) end-to-end: a four-turn lunar-mission Q&A conversation with conversation memory threaded through state, one mid-conversation turn attaching a photograph via `ImageURLBlockTemplate`, the agent processing the multimodal turn naturally without changing the chat-history shape. Complementary to the tool-use example; chat history threading and tool calling are separate primitives. - **`docs/examples/index.md` catalog now lists the langfuse-observability example.** A pre-existing gap (it was missing from the catalog) caught and fixed alongside the chat-with-multimodal entry. - **PyPI + spec-version shields on the docs homepage.** `docs/index.md` now carries dynamic shields for the published PyPI version and the pinned spec version, sourced from `img.shields.io`. Both auto-update on every publish or spec bump; no maintenance burden. Mirrors the same shield URLs the README already uses. diff --git a/pyproject.toml b/pyproject.toml index 369aa111..c74bdaef 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -9,7 +9,7 @@ build-backend = "hatchling.build" [project] name = "openarmature" -version = "0.11.0" +version = "0.12.0rc1" description = "Workflow framework for LLM pipelines and tool-calling agents." readme = "README.md" requires-python = ">=3.12" diff --git a/src/openarmature/AGENTS.md b/src/openarmature/AGENTS.md index ad39275f..b2add674 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.11.0 (spec v0.46.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.12.0rc1 (spec v0.46.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 diff --git a/src/openarmature/__init__.py b/src/openarmature/__init__.py index dd208399..08cffc43 100644 --- a/src/openarmature/__init__.py +++ b/src/openarmature/__init__.py @@ -24,7 +24,7 @@ sessions opening the project find the bundled docs automatically. """ -__version__ = "0.11.0" +__version__ = "0.12.0rc1" __spec_version__ = "0.46.0" # Proposal 0052 (spec observability §5.1 / §8.4.1): canonical # package-registry name for this implementation. Surfaces on every diff --git a/tests/test_smoke.py b/tests/test_smoke.py index d7899813..5bebcfe4 100644 --- a/tests/test_smoke.py +++ b/tests/test_smoke.py @@ -8,7 +8,7 @@ def test_package_versions() -> None: - assert openarmature.__version__ == "0.11.0" + assert openarmature.__version__ == "0.12.0rc1" assert openarmature.__spec_version__ == "0.46.0" diff --git a/uv.lock b/uv.lock index fec88505..dad32165 100644 --- a/uv.lock +++ b/uv.lock @@ -925,7 +925,7 @@ wheels = [ [[package]] name = "openarmature" -version = "0.11.0" +version = "0.12.0rc1" source = { editable = "." } dependencies = [ { name = "httpx" }, From 3fe6f2593bc8b48321197395b09886cd890884e4 Mon Sep 17 00:00:00 2001 From: chris-colinsky Date: Fri, 5 Jun 2026 18:50:06 -0700 Subject: [PATCH 2/2] Restore empty [Unreleased] heading MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Keep a Changelog convention is to keep an empty [Unreleased] heading at the top of CHANGELOG.md permanently — the first entry of the next release cycle lands there. The release-prep edit replaced the [Unreleased] heading with the versioned header inline, leaving nowhere for the v0.13.0 cycle's first entry to land without manually re-adding the heading. Restored above the [0.12.0] block. --- CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 41c2ac7b..c7043ddc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ All notable changes to `openarmature-python` are documented in this file. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). The package follows [Semantic Versioning](https://semver.org/); pre-1.0 minor bumps may carry behavioral changes per [spec governance](https://github.com/LunarCommand/openarmature-spec/blob/main/GOVERNANCE.md). +## [Unreleased] + ## [0.12.0] — 2026-06-05 Observability release. The pinned spec advances from v0.38.0 to v0.46.0, absorbing eight accepted proposals (0047-0054). Three ship as fully implemented this cycle: proposal 0048 grows a read-symmetric `get_invocation_metadata()` API + a §9 *Queryable observer pattern* concept doc section; proposal 0052 puts `openarmature.implementation.name` + `.version` attribution attributes on every OTel invocation span + every Langfuse Trace; proposal 0054 ships `CompiledGraph.drain_events_for(invocation_id, *, timeout)` as the architectural pair to 0048's §9.4 accumulator lifecycle. Two ship as textual-only acks (0051 Langfuse trace I/O caveat; 0053 §3.4 shared-parent boundary clarification). One Fixed: the retry middleware now resets the invocation-metadata ContextVar between attempts per §3.4. The production-observability example grows the queryable accumulator + drain_events_for pattern end-to-end so the new APIs have a runnable demo.