Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,16 @@ The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). The

### Changed

- **Pinned spec advanced from v0.38.0 to v0.45.0.** Submodule + `[tool.openarmature].spec_version` + `conformance.toml` `spec_pin` advance together. Absorbs seven new proposals (0047-0053) into the conformance manifest. Two of them ship as part of the v0.12.0 cycle as textual-only acknowledgments with no code change required: **proposal 0051** (observability §8.4.1 Langfuse `trace.input` / `trace.output` implementation-surface caveat — documents that vendor SDK round-trip is required to project caller-side trace I/O updates onto the wire; the v0.11.0 (proposal 0043) caller-hook shape already matches the documented behavior) and **proposal 0053** (observability §3.4 shared-parent boundary clarification — tightens the structural-shared-parent classification to predicate the invocation span on whether at least one fan-out or parallel-branches dispatch is on the augmenter's call-stack path; behavior already matches via fixtures 034 + 039). The remaining five proposals are marked `not-yet` in the conformance manifest with roadmap targets: 0048 + 0052 (this v0.12.0 cycle), 0047 + 0049 (v0.13.0 LLM provider hardening batch), 0050 (v0.14.0 retry & reliability batch).
- **Pinned spec advanced from v0.38.0 to v0.46.0.** Submodule + `[tool.openarmature].spec_version` + `conformance.toml` `spec_pin` advance together. Absorbs eight new proposals (0047-0054) into the conformance manifest. Two of them ship as part of the v0.12.0 cycle as textual-only acknowledgments with no code change required: **proposal 0051** (observability §8.4.1 Langfuse `trace.input` / `trace.output` implementation-surface caveat — documents that vendor SDK round-trip is required to project caller-side trace I/O updates onto the wire; the v0.11.0 (proposal 0043) caller-hook shape already matches the documented behavior) and **proposal 0053** (observability §3.4 shared-parent boundary clarification — tightens the structural-shared-parent classification to predicate the invocation span on whether at least one fan-out or parallel-branches dispatch is on the augmenter's call-stack path; behavior already matches via fixtures 034 + 039). Two more ship as implemented in this cycle: **proposal 0048** (read-symmetric metadata + queryable observer pattern docs — see *Added* below) and **proposal 0052** (implementation attribution attributes — landing in a follow-on PR of this cycle). The remaining proposals are marked `not-yet` in the conformance manifest with roadmap targets: 0047 + 0049 (v0.13.0 LLM provider hardening batch), 0050 (v0.14.0 retry & reliability batch), 0054 (per-invocation observer event drain — bundled into this v0.12.0 cycle alongside 0048 per the §9.4 accumulator-lifecycle pairing; lands in a follow-on PR).
- **README and docs homepage refreshed around reasons-to-choose.** Replaced the 10-bullet "Why OpenArmature" feature inventory in `README.md` with 5 differentiating reasons (LLM-infused workflows to agents on one engine; crash-safe resume by contract; destination-pluggable observability with OTel + Langfuse, no SaaS lock-in; compile-time topology checks; spec + conformance). The docs homepage (`docs/index.md`) card grid carries the same five plus a sixth card retained from the previous grid for async-first / LLM-agnostic: workflows-to-agents, crash-safe, pluggable observability, bad-graphs-don't-compile, parallelism (fan-out + parallel-branches + nested correctness), async-first.
- **Docs sweep: stale references and em-dash normalization.** Fixed three definite stale references (`spec_version='0.26.0'` in the Langfuse example output now reads `'0.38.0'`; the dangling `v0.16.1` qualifier dropped from the parallel-branches concept page; `compiled.attach_observer` corrected to `graph.attach_observer` in `non-obvious-shapes.md` for variable-name consistency with the rest of the docs). Swept em dashes out of the user-facing docs (130 instances across 17 files) per the convention set during the patterns expansion. mkdocs strict build clean; no broken intra-docs links.
- **The checkpointing-and-migration example grows a crash-and-resume drama.** The first invoke of the v1 graph now hits a simulated transient failure inside `size_crew` (raises a `RuntimeError` on its first attempt only). The example catches `NodeException` at the `invoke()` boundary, prints what's saved on disk (`define_objective`'s position is already in `completed_positions`), then re-invokes with `resume_invocation=<id>`. The retried `size_crew` succeeds, `draft_timeline` runs, and the pipeline finishes - dramatizing the synchronous-checkpoint-by-contract reliability claim from the README pitch. The existing v1->v2 migration phase rides on top of the crash-survived checkpoint, so both reliability stories compose in one demo. Walk-through doc rewritten to cover both phases.
- **Examples renamed and catalog reorganized by topic.** All 13 example directories drop their numeric prefixes (`examples/00-hello-world/` -> `examples/hello-world/`, etc.); the corresponding `docs/examples/NN-name.md` files do the same. The catalog at `docs/examples/index.md` and the mkdocs nav are regrouped into seven topical sections: Foundations, Composition, Concurrency, Prompts, Tool use, Reliability, Observability. Catalog entries and walk-through H1s drop the number prefix; cross-references between examples are rewritten by-name (no more "Example 03"). The `examples/README.md` is brought up to date with the post-v0.11.0 catalog (entries for chat-with-multimodal, langfuse-observability, production-observability now present) under the same grouping. `tests/test_examples_smoke.py`'s DEMOS list updated; the pytest parametrize IDs follow the new names. Em dashes scrubbed from the example sources and `examples/README.md` to match the convention established in the v0.11.0-cycle docs sweep.

### Added

- **`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.
- **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.
Expand Down
64 changes: 58 additions & 6 deletions conformance.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@

[manifest]
implementation = "openarmature-python"
spec_pin = "v0.45.0"
spec_pin = "v0.46.0"

# Status values:
# implemented — shipped behavior matches the proposal's contract
Expand Down Expand Up @@ -248,12 +248,54 @@ since = "0.11.0"
[proposals."0047"]
status = "not-yet"

# Spec v0.40.0 (proposal 0048). Read-symmetric invocation metadata
# (``get_invocation_metadata()`` symmetric to ``set_invocation_metadata()``)
# + queryable observer pattern blessing. Queued for v0.12.0 (this
# cycle); will flip to ``implemented`` in the implementation PR.
# Spec v0.40.0 (proposal 0048). Read-symmetric invocation metadata.
# Adds ``get_invocation_metadata()`` symmetric to the existing
# ``set_invocation_metadata()`` write API. The python implementation
# satisfies most of the §3.4 read contract via the pre-existing
# ``current_invocation_metadata`` machinery: returns ``MappingProxyType``
# snapshot of the current async context's view, silent no-op outside
# an invocation, no observer emission, per-async-context scoping under
# fan-out and parallel-branches (inherited from 0034/0040/0045).
# ``get_invocation_metadata`` lands as the canonical spec-idiomatic
# public name; ``current_invocation_metadata`` stays as a stable
# alias (Option A per the proposal-0048-implementation coord thread).
# Plus a new §9 *Queryable observer pattern* section in
# ``docs/concepts/observability.md`` covering the §9.1 read-method
# contract, §9.2 async-safety, §9.3 three-channel guidance, §9.4
# lifecycle. §9 is convention-only per spec — no new abstract
# surface on ``Observer``.
#
# Open gap: §3.4 *per-attempt scoping under retry middleware* is NOT
# enforced in v0.12.0 — the retry middleware
# (``src/openarmature/graph/middleware/retry.py``) manages the
# ``attempt_index`` ContextVar but does NOT reset the
# invocation-metadata ContextVar between attempts. A write from a
# failed attempt remains visible on the next attempt. Closing this
# gap is a follow-on PR (engine-side reset of ``_invocation_metadata_var``
# around each retry iteration + a pinning unit test against spec
# fixture 045's contract).
#
# Conformance fixtures 043-049 introduce new directive shapes
# (augment_metadata / capture_invocation_metadata_into /
# capture_queryable_observer_read_into / per_attempt_behavior +
# queryable_observers / inner_subgraphs / caller_metadata /
# direct_call / sequential_invocations top-level keys) that the
# cross-capability parser doesn't model; fixture-shape activation is
# queued for a future PR. The shipped portion of the contract is
# pinned by explicit unit tests:
# - alias identity + roundtrip + immutable-mapping return:
# ``tests/unit/test_observability_metadata.py::
# test_get_invocation_metadata_is_same_callable_as_current``,
# ``::test_get_invocation_metadata_roundtrip_baseline_plus_augment``,
# ``::test_get_invocation_metadata_returns_immutable_mapping_outside_invocation``;
# - mid-invocation augmentation visible to subsequent reads:
# ``::test_mid_invocation_augmentation_persists_to_next_node``;
# - outside-invocation empty:
# ``::test_current_invocation_metadata_empty_outside_invocation``.
[proposals."0048"]
status = "not-yet"
status = "partial"
since = "0.12.0"
note = "Read API + §9 queryable observer pattern docs ship in v0.12.0. Spec §3.4 per-attempt scoping under retry middleware is NOT yet enforced — the python retry middleware does not reset the invocation-metadata ContextVar between attempts, so a failed attempt's writes remain visible on retry. Per-async-context scoping under fan-out and parallel-branches is fully implemented. Closing the retry-side gap is tracked for a follow-on PR."

# Spec v0.41.0 (proposal 0049). Typed LLM Completion Event — first
# typed event variant on the observer event union. Queued for
Expand Down Expand Up @@ -296,3 +338,13 @@ status = "not-yet"
[proposals."0053"]
status = "textual-only"
since = "0.12.0"

# Spec v0.46.0 (proposal 0054). Per-invocation observer event drain
# (``drain_events_for(invocation_id, *, timeout) -> DrainSummary``).
# Bundled into v0.12.0 per the 2026-06-03 decision in the
# proposal-0048-implementation coord thread — 0054 directly resolves
# the §9.4 accumulator-lifecycle synchronization race that 0048's
# queryable observer pattern would otherwise expose. Lands in
# PR 2b (after this PR's 0048 implementation).
[proposals."0054"]
status = "not-yet"
Loading