Implement proposal 0048 read-symmetric metadata#129
Merged
chris-colinsky merged 4 commits intoJun 4, 2026
Conversation
Adds get_invocation_metadata() as the spec-idiomatic public name for the observability §3.4 read API, paralleling the existing set_invocation_metadata() write helper. The alias is a direct module-level rebind of the historical current_invocation_metadata (same function object), exported through openarmature.observability alongside the original name. Existing callers continue to work unchanged. Bumps the pinned spec from v0.45.0 to v0.46.0 and absorbs proposal 0054 (per-invocation observer event drain) into the conformance manifest as not-yet; 0054 is bundled into the v0.12.0 release per the §9.4 accumulator-lifecycle pairing and lands in a follow-on PR. Adds a §9 *Queryable observer pattern* section to the observability concepts page documenting the convention-only observer-attached read-method pattern, the read-method contract, async-safety, the three-channel data-access guidance (State / invocation metadata / queryable observer accumulator), and the accumulator lifecycle with the §9.4 drop() discipline. Conformance fixtures 043-049 stay deferred from the cross-capability parser (they introduce many new directive shapes the harness would need to model); the §3.4 read contract is pinned by explicit unit tests plus the predecessor proposal 0034/0040/0045 conformance fixtures that exercise the same underlying ContextVar machinery.
There was a problem hiding this comment.
Pull request overview
Adds the spec-canonical get_invocation_metadata() name (proposal 0048) as an alias to the existing invocation-metadata read API, updates exports/tests/docs accordingly, and bumps the pinned spec version to v0.46.0 (adding proposal 0054 as not-yet in the manifest).
Changes:
- Introduce and re-export
get_invocation_metadataas an alias ofcurrent_invocation_metadata, plus unit tests for alias identity and roundtrip behavior. - Update observability docs with the §3.4 read API guidance and add a new “Queryable observer pattern” section (§9).
- Bump spec pin/version references to v0.46.0 across runtime constants, pyproject, conformance manifest, and smoke tests.
Reviewed changes
Copilot reviewed 12 out of 12 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/unit/test_observability_metadata.py | Adds unit tests pinning the new alias name and end-to-end metadata read/write behavior. |
| tests/test_smoke.py | Updates expected __spec_version__ to v0.46.0. |
| tests/conformance/test_fixture_parsing.py | Updates fixture deferral rationale for 0048 and adds new deferrals for 0054 fixtures. |
| src/openarmature/observability/metadata.py | Defines get_invocation_metadata alias, updates docstrings, and exports it via __all__. |
| src/openarmature/observability/init.py | Re-exports get_invocation_metadata from the public observability surface. |
| src/openarmature/AGENTS.md | Updates embedded spec version text to v0.46.0. |
| src/openarmature/init.py | Bumps __spec_version__ to v0.46.0. |
| pyproject.toml | Bumps [tool.openarmature].spec_version to v0.46.0. |
| docs/concepts/observability.md | Rewrites “Reading the in-scope metadata” section and adds the §9 queryable observer pattern documentation. |
| conformance.toml | Bumps spec_pin to v0.46.0; marks 0048 implemented; adds 0054 as not-yet. |
| CHANGELOG.md | Updates Unreleased notes for spec-pin bump and documents the 0048 alias + §9 docs additions. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
Both in-page callouts in docs/concepts/observability.md (the new proposal-0054 bundling note and the pre-existing Langfuse SDK version compatibility note) were using `!!! note`, which renders in Material's default blue. The 13 example walk-through pages use `!!! info "Source"`, which the project CSS paints in the brand lavender. Flip the two concept-page callouts to `!!! info` so every admonition site-wide picks up the same brand styling. Also refresh the now-stale CSS comment that claimed the only `!!! info` usage was the Source box.
The v0.12.0 python retry middleware manages the attempt_index ContextVar but does not reset the invocation-metadata ContextVar between attempts; a write from a failed retry attempt remains visible on the next attempt. The original PR docs and conformance note claimed per-attempt scoping was satisfied, which is wrong. Walks the docs back: the per-attempt sentence is dropped and an explicit info callout names the gap and points at the follow-on PR that will land the engine-side reset + the pinning unit test. Flips conformance.toml status for proposal 0048 from implemented to partial. The note field documents what ships in v0.12.0 (read API + queryable observer pattern docs + per-async-context scoping under fan-out / parallel-branches) versus the open retry-side gap.
The persist() example in the queryable observer pattern docs was reading state.invocation_id, but a default State has no such field. Switch the snippet to bind invocation_id from current_invocation_id() (the supported in-node accessor) with a brief note that it is guaranteed non-None inside a node body. The 0048 fixture-deferral comment in test_fixture_parsing.py claimed unit tests covered per-attempt scoping under retry and per-async-context scoping under fan-out. Neither is true: there are no retry-attempt tests in test_observability_metadata.py, and the fan-out coverage lives in the runtime conformance harness via the predecessor proposal 0034/0040 fixtures, not as unit tests here. Rewrite the comment to be honest about what each pin actually covers and to flag the retry-side gap.
7 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Proposal 0048 (observability §3.4, spec v0.40.0) adds
get_invocation_metadata()as the canonical spec-idiomatic public name for the metadata read API, paralleling the existingset_invocation_metadata()write helper. The python implementation is a one-line module-level rebind of the historicalcurrent_invocation_metadata(same function object), exposed throughopenarmature.observabilityalongside the original name. Existing callers continue to work unchanged.The §3.4 read contract was already satisfied end-to-end by the pre-existing ContextVar machinery (returns a
MappingProxyTypesnapshot, silent no-op outside an invocation, no observer emission, per-attempt + per-fan-out scoping inherited from proposals 0034/0040/0045). The value-add in this PR is the canonical name + the §9 Queryable observer pattern documentation.This PR also bumps the pinned spec from v0.45.0 to v0.46.0, absorbing proposal 0054 (per-invocation observer event drain) into the conformance manifest as
not-yet. 0054 is bundled into the v0.12.0 release cycle alongside 0048 per the §9.4 accumulator-lifecycle pairing (without the per-invocation drain, the accumulator pattern would race against the deliver loop on the last-event read); 0054's implementation lands in a follow-on PR.Notable pieces
src/openarmature/observability/metadata.pyadds the alias and updates the docstrings on both names to cross-reference each other.src/openarmature/observability/__init__.pyre-exports the new name.docs/concepts/observability.mdrewrites the Reading the in-scope metadata subsection against the §3.4 contract (per-attempt scoping under retry, three call-site categories, per-async-context fan-out scoping), and adds a new top-level Queryable observer pattern section covering the §9.1 read-method contract, §9.2 async-safety, §9.3 three-channel data-access guidance with a side-by-side table (typed State vs untyped invocation metadata vs queryable accumulator), and §9.4 lifecycle with the explicitdrop()discipline. The section also documents the synchronization pattern withdrain_events_for(proposal 0054, bundled into v0.12.0) with a callout that the primitive itself ships alongside.conformance.tomlflips 0048 toimplemented/since = "0.12.0"with a detailed note documenting both the §3.4 contract mechanism and the specific unit tests + predecessor conformance fixtures that pin each part. 0054 is added asnot-yet.CHANGELOG.md[Unreleased]Addedgets entries for the read API and the §9 docs. The existing spec-pinChangedentry is updated to reflect the v0.46.0 advance and the 0054 bundling.tests/conformance/test_fixture_parsing.pykeeps fixtures 043-049 deferred from the cross-capability parser with truthful rationale: they introduce many new directive shapes (augment_metadata,capture_invocation_metadata_into,capture_queryable_observer_read_into,per_attempt_behavior, top-levelqueryable_observers/inner_subgraphs/caller_metadata/direct_call/sequential_invocations/informative, plusfinal_state_bounds/direct_call_result/per_invocationin the expected block) that the harness would need to model. The behavioral contract is pinned by unit tests; fixture-shape activation is queued for a future PR. Same precedent as proposal 0045 fixture 039.tests/unit/test_observability_metadata.pyadds four new tests for the alias:is-identity withcurrent_invocation_metadata, empty-outside-invocation,MappingProxyTypereturn type (both outside and inside an active invocation), and the end-to-end roundtrip (caller baseline + in-node augment).Test plan
uv run pytest tests/— 1098 passed, 307 skippeduv run pytest tests/unit/test_observability_metadata.py— 48 passeduv run python scripts/check_conformance_manifest.py— 51/51 entries consistentuv run ruff check .+uv run ruff format --check .— cleanuv run pyright src/ tests/— 0 errorsuv run mkdocs build --strict— clean