From 25f555ee430e240060c22082fb0cc206ad3b2e76 Mon Sep 17 00:00:00 2001 From: chris-colinsky Date: Wed, 3 Jun 2026 15:45:59 -0700 Subject: [PATCH 1/2] Bump spec pin to v0.45.0 + textual-only acks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit First PR of the v0.12.0 cycle. Mechanical pin bump from spec v0.38.0 → v0.45.0, picking up seven new accepted proposals: - 0047 wire-byte stability (queued for v0.13.0) - 0048 read-symmetric metadata + queryable observer (next PR in this cycle) - 0049 typed LLM completion event (v0.13.0) - 0050 retry & failure-isolation primitives (v0.14.0) - 0051 Langfuse trace I/O implementation-surface caveat (textual-only; pin bump documents the caveat, the v0.11.0 proposal 0043 caller-hook shape already matches) - 0052 implementation attribution attributes (PR 3 of this cycle) - 0053 §3.4 shared-parent boundary clarification (textual-only; behavior already matches fixtures 034 + 039) Mechanical changes: - Submodule advances to v0.45.0 tag (SHA defb8f9). - ``pyproject.toml`` ``[tool.openarmature].spec_version`` → ``"0.45.0"``; ``openarmature.__spec_version__`` matches. - ``conformance.toml`` ``spec_pin`` → ``"v0.45.0"``; seven new entries (0047-0053): 0051 + 0053 as ``textual-only`` with ``since = "0.12.0"``; the rest as ``not-yet``. - ``CHANGELOG.md`` ``[Unreleased]`` notes the pin bump and the two textual-only acks. - ``src/openarmature/AGENTS.md`` regenerated against the new pin. Deferral plumbing for the not-yet implementation surface: - ``tests/conformance/_deferral.py`` (new): factors the fixture-level ``skip_if_deferred(fixture_id, deferrals)`` helper out of the per-file ``_DEFERRED_FIXTURES`` skip-call sites so the message format stays consistent. - ``tests/conformance/test_fixture_parsing.py``: 29 new fixtures added to ``_DEFERRED_FIXTURES`` with per-proposal deferral notes; both skip-call sites now use the shared helper. - ``tests/conformance/test_llm_provider.py``: 5 new fixtures deferred (proposals 0047, 0050); skip-call site moved to the helper. - ``tests/conformance/test_prompt_management.py``: new ``_DEFERRED_FIXTURES`` dict gates fixture 032 (cross-variable-substring-stability) on proposal 0047; skip-call site uses the helper. - ``tests/conformance/test_observability.py`` fixture-028 runner: case-level skip-list for the two new ``rejects_reserved_oa_name_implementation_*`` cases (proposal 0052; lands in PR 3 of this cycle). Doesn't use the shared helper because the case-level skip is ``continue`` inside an async loop, not ``pytest.skip``. - ``tests/test_smoke.py::test_package_versions``: expected ``__spec_version__`` updated to "0.45.0". Verified: 1088 passed, 294 skipped (the deferred fixtures); conformance guard clean; mkdocs strict clean; ruff + pyright clean. --- CHANGELOG.md | 1 + conformance.toml | 58 ++++++++++- openarmature-spec | 2 +- pyproject.toml | 2 +- src/openarmature/AGENTS.md | 4 +- src/openarmature/__init__.py | 2 +- tests/conformance/_deferral.py | 39 +++++++ tests/conformance/test_fixture_parsing.py | 110 +++++++++++++++++++- tests/conformance/test_llm_provider.py | 18 +++- tests/conformance/test_observability.py | 20 ++++ tests/conformance/test_prompt_management.py | 18 ++++ tests/test_smoke.py | 2 +- 12 files changed, 263 insertions(+), 13 deletions(-) create mode 100644 tests/conformance/_deferral.py diff --git a/CHANGELOG.md b/CHANGELOG.md index e317d01d..bd33138d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ 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). - **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=`. 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. diff --git a/conformance.toml b/conformance.toml index 1a6f7af8..7c13c032 100644 --- a/conformance.toml +++ b/conformance.toml @@ -32,7 +32,7 @@ [manifest] implementation = "openarmature-python" -spec_pin = "v0.38.0" +spec_pin = "v0.45.0" # Status values: # implemented — shipped behavior matches the proposal's contract @@ -240,3 +240,59 @@ since = "0.11.0" [proposals."0046"] status = "implemented" since = "0.11.0" + +# Spec v0.39.0 (proposal 0047). Implicit prefix-cache wire-byte +# stability. Cross-provider invariant requiring intra-impl byte +# equality across calls with equivalent inputs. Queued for v0.13.0 +# alongside 0049 (LLM provider hardening + typed event batch). +[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. +[proposals."0048"] +status = "not-yet" + +# Spec v0.41.0 (proposal 0049). Typed LLM Completion Event — first +# typed event variant on the observer event union. Queued for +# v0.13.0 alongside 0047. +[proposals."0049"] +status = "not-yet" + +# Spec v0.42.0 (proposal 0050). Retry & degradation primitives — +# failure-isolation middleware + call-level retry. Queued for +# v0.14.0 (largest single piece in the roadmap). +[proposals."0050"] +status = "not-yet" + +# Spec v0.43.0 (proposal 0051). Langfuse trace.input/trace.output +# implementation-surface caveat. Purely textual: documents that the +# Langfuse SDK's ``LangfuseClient`` API requires a trace object +# round-trip to project caller-side ``trace.input`` / ``trace.output`` +# updates onto the wire. No module-level change required; the python +# implementation's existing v0.11.0 (proposal 0043) shape already +# matches the documented behavior. +[proposals."0051"] +status = "textual-only" +since = "0.12.0" + +# Spec v0.44.0 (proposal 0052). Implementation attribution attributes +# (``openarmature.implementation.name`` + ``openarmature.implementation.version`` +# on every invocation span). Queued for v0.12.0 (this cycle); will +# flip to ``implemented`` in the implementation PR. +[proposals."0052"] +status = "not-yet" + +# Spec v0.45.0 (proposal 0053). §3.4 shared-parent boundary +# clarification. Purely textual: 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. Implementation-side: behavior already matches the +# clarified contract via fixtures 034 (outermost-serial augmentation +# reaches the invocation span) and 039 (nested cases do not). No +# module-level change required. +[proposals."0053"] +status = "textual-only" +since = "0.12.0" diff --git a/openarmature-spec b/openarmature-spec index 01eb9bfd..defb8f98 160000 --- a/openarmature-spec +++ b/openarmature-spec @@ -1 +1 @@ -Subproject commit 01eb9bfd6d5a71eb20be9f121d8bf67de31e1143 +Subproject commit defb8f98e7a5bb6d44da7a023fcc2965223ebbf0 diff --git a/pyproject.toml b/pyproject.toml index 87b98301..cad793ff 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.38.0" +spec_version = "0.45.0" [dependency-groups] dev = [ diff --git a/src/openarmature/AGENTS.md b/src/openarmature/AGENTS.md index 283601a1..22fe6342 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.38.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.11.0 (spec v0.45.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 @@ -10,7 +10,7 @@ OpenArmature is a workflow framework for LLM pipelines and tool-calling agents: ## Capability contracts -_Sourced from openarmature-spec v0.38.0. Each entry below reproduces §1 (Purpose) and §2 (Concepts) of the capability's `spec.md`. For the full spec text (execution model, error semantics, determinism, observer hooks, etc.) see the linked docs site._ +_Sourced from openarmature-spec v0.45.0. Each entry below reproduces §1 (Purpose) and §2 (Concepts) of the capability's `spec.md`. 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 bd9e0180..54d54a4a 100644 --- a/src/openarmature/__init__.py +++ b/src/openarmature/__init__.py @@ -25,4 +25,4 @@ """ __version__ = "0.11.0" -__spec_version__ = "0.38.0" +__spec_version__ = "0.45.0" diff --git a/tests/conformance/_deferral.py b/tests/conformance/_deferral.py new file mode 100644 index 00000000..cd95339a --- /dev/null +++ b/tests/conformance/_deferral.py @@ -0,0 +1,39 @@ +"""Shared deferral plumbing for conformance test files. + +Each fixture-level conformance test (``test_fixture_parsing``, +``test_llm_provider``, ``test_prompt_management``) maintains its own +``_DEFERRED_FIXTURES`` dict mapping fixture IDs to human-readable +reasons. Entries come and go as proposals land in their implementing +PRs. + +This helper centralizes the skip-call site so the format +(``f"{fixture_id}: {reason}"``) stays consistent across files and so +future tweaks to the message shape land in one place. + +The case-level skip inside ``_run_fixture_028`` in +``test_observability.py`` deliberately doesn't use this helper — it +``continue``\\s inside an async loop iterating sub-cases of a single +fixture rather than skipping the outer pytest test. +""" + +from __future__ import annotations + +from collections.abc import Mapping + +import pytest + + +def skip_if_deferred(fixture_id: str, deferrals: Mapping[str, str]) -> None: + """Raise :func:`pytest.skip` if ``fixture_id`` appears in ``deferrals``. + + Standard call site at the top of a conformance test that processes + one fixture per pytest test:: + + @pytest.mark.parametrize(...) + def test_xxx_fixture(fixture_path: Path) -> None: + fixture_id = _fixture_id(fixture_path) + skip_if_deferred(fixture_id, _DEFERRED_FIXTURES) + # ... rest of the test ... + """ + if fixture_id in deferrals: + pytest.skip(f"{fixture_id}: {deferrals[fixture_id]}") diff --git a/tests/conformance/test_fixture_parsing.py b/tests/conformance/test_fixture_parsing.py index 1d1b3d84..e2eac745 100644 --- a/tests/conformance/test_fixture_parsing.py +++ b/tests/conformance/test_fixture_parsing.py @@ -14,6 +14,7 @@ import pytest +from ._deferral import skip_if_deferred from .harness import discover_fixtures, load_fixture @@ -243,6 +244,109 @@ def _id(case: tuple[str, Path]) -> str: # Proposal 0046 (chat-prompt rendering, v0.38.0) — fixtures 017-031 # activate in PR 12 against the prompt-management harness's # extended ``chat_template`` + ``placeholders`` directive shapes. + # ----- v0.12.0 cycle spec-pin bump (v0.38.0 -> v0.45.0) ---------------- + # Proposal 0047 (implicit prefix-cache wire-byte stability, v0.39.0) + # — fixtures 054-055 require the wire-byte hashing directive shape. + # Queued for v0.13.0 LLM provider hardening batch. + "llm-provider/054-openai-wire-byte-stability": ("Proposal 0047 wire-byte stability; queued for v0.13.0"), + "llm-provider/055-anthropic-wire-byte-stability": ( + "Proposal 0047 wire-byte stability; queued for v0.13.0" + ), + # Proposal 0047 also adds cache attribute emission fixtures + # (observability/040-042). Same v0.13.0 batch. + "observability/040-llm-cache-attribute-emission": ( + "Proposal 0047 cache attribute emission; queued for v0.13.0" + ), + "observability/041-llm-cache-attribute-absence": ( + "Proposal 0047 cache attribute emission; queued for v0.13.0" + ), + "observability/042-llm-cache-attribute-reported-zero": ( + "Proposal 0047 cache attribute emission; queued for v0.13.0" + ), + # Proposal 0048 (read-symmetric metadata + queryable observer, v0.40.0) + # — fixtures 043-049 require the ``capture_invocation_metadata_into`` / + # ``augment_metadata`` / queryable-observer directive shapes. Lands + # in PR 2 of the v0.12.0 cycle along with the implementation; this + # PR-1 deferral keeps the parse tests green in the meantime. + "observability/043-get-invocation-metadata-roundtrip": ( + "Proposal 0048 read-symmetric metadata; lands in PR 2 of v0.12.0" + ), + "observability/044-get-invocation-metadata-fan-out-scoping": ( + "Proposal 0048 read-symmetric metadata; lands in PR 2 of v0.12.0" + ), + "observability/045-get-invocation-metadata-retry-scoping": ( + "Proposal 0048 read-symmetric metadata; lands in PR 2 of v0.12.0" + ), + "observability/046-get-invocation-metadata-outside-invocation": ( + "Proposal 0048 read-symmetric metadata; lands in PR 2 of v0.12.0" + ), + "observability/047-queryable-observer-pattern": ( + "Proposal 0048 queryable observer pattern; lands in PR 2 of v0.12.0" + ), + "observability/048-queryable-observer-async-safety": ( + "Proposal 0048 queryable observer pattern; lands in PR 2 of v0.12.0" + ), + "observability/049-queryable-observer-lifecycle-drop": ( + "Proposal 0048 queryable observer pattern; lands in PR 2 of v0.12.0" + ), + # Proposal 0049 (typed LLM completion event, v0.41.0) — fixtures + # 050-056 require the ``LlmCompletionEvent`` typed-event directive + # shape. Queued for v0.13.0 LLM provider hardening batch. + "observability/050-llm-completion-event-dispatch": ( + "Proposal 0049 typed LLM completion event; queued for v0.13.0" + ), + "observability/051-llm-completion-event-type-discrimination": ( + "Proposal 0049 typed LLM completion event; queued for v0.13.0" + ), + "observability/052-llm-completion-event-caller-metadata-opt-in": ( + "Proposal 0049 typed LLM completion event; queued for v0.13.0" + ), + "observability/053-llm-completion-event-no-event-on-failure": ( + "Proposal 0049 typed LLM completion event; queued for v0.13.0" + ), + "observability/054-llm-completion-event-fan-out-index-population": ( + "Proposal 0049 typed LLM completion event; queued for v0.13.0" + ), + "observability/055-llm-completion-event-branch-name-population": ( + "Proposal 0049 typed LLM completion event; queued for v0.13.0" + ), + "observability/056-llm-completion-event-strict-serial-ordering": ( + "Proposal 0049 typed LLM completion event; queued for v0.13.0" + ), + # Proposal 0050 (failure-isolation middleware + call-level retry, + # v0.42.0) — llm-provider fixtures 056-058 (call-level retry) and + # pipeline-utilities fixtures 058-063 (failure-isolation + # middleware) require new directive shapes. Queued for v0.14.0 + # retry & reliability primitives batch. + "llm-provider/056-call-level-retry-transient": ("Proposal 0050 call-level retry; queued for v0.14.0"), + "llm-provider/057-call-level-retry-exhaustion": ("Proposal 0050 call-level retry; queued for v0.14.0"), + "llm-provider/058-call-level-retry-non-transient-no-retry": ( + "Proposal 0050 call-level retry; queued for v0.14.0" + ), + "pipeline-utilities/058-failure-isolation-static-degraded": ( + "Proposal 0050 failure-isolation middleware; queued for v0.14.0" + ), + "pipeline-utilities/059-failure-isolation-callable-degraded": ( + "Proposal 0050 failure-isolation middleware; queued for v0.14.0" + ), + "pipeline-utilities/060-failure-isolation-predicate-filtering": ( + "Proposal 0050 failure-isolation middleware; queued for v0.14.0" + ), + "pipeline-utilities/061-failure-isolation-retry-three-piece-composition": ( + "Proposal 0050 failure-isolation middleware; queued for v0.14.0" + ), + "pipeline-utilities/062-failure-isolation-on-caught-callback": ( + "Proposal 0050 failure-isolation middleware; queued for v0.14.0" + ), + "pipeline-utilities/063-failure-isolation-default-predicate-bare-exception": ( + "Proposal 0050 failure-isolation middleware; queued for v0.14.0" + ), + # Proposal 0052 (implementation attribution attributes, v0.44.0) + # — observability/059 is the Langfuse-side mapping fixture. Lands + # in PR 3 of the v0.12.0 cycle along with the implementation. + "observability/059-implementation-attribution-langfuse": ( + "Proposal 0052 implementation attribution; lands in PR 3 of v0.12.0" + ), } @@ -260,8 +364,7 @@ def test_fixture_parses(case: tuple[str, Path]) -> None: ``GraphFixture`` based on top-level keys; ``extra="forbid"`` rejects any unknown top-level field.""" case_id = _id(case) - if case_id in _DEFERRED_FIXTURES: - pytest.skip(f"{case_id}: {_DEFERRED_FIXTURES[case_id]}") + skip_if_deferred(case_id, _DEFERRED_FIXTURES) _, path = case load_fixture(path) @@ -272,8 +375,7 @@ def test_fixture_round_trips(case: tuple[str, Path]) -> None: Phase 0 per the implementation plan: catches dropped fields the user intended to use later.""" case_id = _id(case) - if case_id in _DEFERRED_FIXTURES: - pytest.skip(f"{case_id}: {_DEFERRED_FIXTURES[case_id]}") + skip_if_deferred(case_id, _DEFERRED_FIXTURES) _, path = case parsed = load_fixture(path) dumped = parsed.model_dump(exclude_none=True) diff --git a/tests/conformance/test_llm_provider.py b/tests/conformance/test_llm_provider.py index 6c4279d7..f56a1983 100644 --- a/tests/conformance/test_llm_provider.py +++ b/tests/conformance/test_llm_provider.py @@ -47,6 +47,7 @@ UserMessage, ) +from ._deferral import skip_if_deferred from .harness import ( assert_error_carries, assert_response_format_absent, @@ -94,6 +95,20 @@ "051-gemini-structured-output-fallback": "Gemini provider not implemented (0038 not-yet)", "052-gemini-thought-signature-round-trip": "Gemini provider not implemented (0038 not-yet)", "053-cross-provider-signature-strip": "Gemini provider not implemented (0038 not-yet)", + # ----- v0.12.0 cycle spec-pin bump (v0.38.0 -> v0.45.0) ------------- + # Proposal 0047 (implicit prefix-cache wire-byte stability, v0.39.0) + # — wire-byte hashing across providers. Queued for v0.13.0 LLM + # provider hardening batch. + "054-openai-wire-byte-stability": ("Proposal 0047 wire-byte stability; queued for v0.13.0"), + "055-anthropic-wire-byte-stability": ( + "Proposal 0047 wire-byte stability; queued for v0.13.0 (also Anthropic-pending)" + ), + # Proposal 0050 (call-level retry, v0.42.0) — three fixtures + # exercise the new ``retry`` kwarg on ``complete()``. Queued for + # v0.14.0 retry & reliability primitives batch. + "056-call-level-retry-transient": ("Proposal 0050 call-level retry; queued for v0.14.0"), + "057-call-level-retry-exhaustion": ("Proposal 0050 call-level retry; queued for v0.14.0"), + "058-call-level-retry-non-transient-no-retry": ("Proposal 0050 call-level retry; queued for v0.14.0"), } @@ -458,8 +473,7 @@ def _assert_raises_matches( @pytest.mark.parametrize("fixture_path", _fixture_paths(), ids=_fixture_id) async def test_llm_provider_fixture(fixture_path: Path) -> None: fixture_id = fixture_path.stem - if fixture_id in _DEFERRED_FIXTURES: - pytest.skip(f"{fixture_id}: {_DEFERRED_FIXTURES[fixture_id]}") + skip_if_deferred(fixture_id, _DEFERRED_FIXTURES) spec = _load(fixture_path) if "cases" in spec: diff --git a/tests/conformance/test_observability.py b/tests/conformance/test_observability.py index 40ad5741..db8b1bee 100644 --- a/tests/conformance/test_observability.py +++ b/tests/conformance/test_observability.py @@ -892,9 +892,29 @@ async def _run_fixture_028(spec: Mapping[str, Any]) -> None: LangfuseObserver, ) + # Case-level deferrals for fixture 028. The spec extends the + # fixture with new negative-control cases as new reserved keys + # land in §3.4; impl-side coverage for those cases lands in the + # PR that implements the corresponding key reservation. + _deferred_cases: dict[str, str] = { + # Proposal 0052 (implementation attribution, spec v0.44.0) + # extends the reserved-set 24 → 26 names with + # ``implementation_name`` / ``implementation_version``. + # Coverage lands in PR 3 of the v0.12.0 cycle alongside the + # ``openarmature.implementation.*`` invocation-span attribute + # emission. + "rejects_reserved_oa_name_implementation_name": ( + "Proposal 0052 implementation attribution; lands in PR 3 of v0.12.0" + ), + "rejects_reserved_oa_name_implementation_version": ( + "Proposal 0052 implementation attribution; lands in PR 3 of v0.12.0" + ), + } cases = cast("list[dict[str, Any]]", spec["cases"]) for case in cases: case_name = cast("str", case["name"]) + if case_name in _deferred_cases: + continue try: # Build a minimal graph from the case's nodes/edges. The # fixture's node is a noop update — we never expect it to diff --git a/tests/conformance/test_prompt_management.py b/tests/conformance/test_prompt_management.py index 84756f63..d2e6884c 100644 --- a/tests/conformance/test_prompt_management.py +++ b/tests/conformance/test_prompt_management.py @@ -40,6 +40,7 @@ TextPrompt, ) +from ._deferral import skip_if_deferred from .harness.loader import CONFORMANCE_ROOT from .harness.prompt_management import ( FixtureBackendSpec, @@ -553,8 +554,25 @@ def _message_to_dict_for_compare(message: Message) -> dict[str, Any]: return dumped +# Fixtures whose implementation lands in a later PR. Skip-marked so a +# green test run at this commit means "everything we claim to implement +# passes." Each subsequent PR drops its own rows as it lands the +# underlying support. +_DEFERRED_FIXTURES: dict[str, str] = { + # Proposal 0047 (implicit prefix-cache wire-byte stability, spec + # v0.39.0) adds an ``expected_shared_prefix`` directive — multi- + # render byte-equality check on the shared template prefix. + # Queued for v0.13.0 LLM provider hardening batch. + "032-cross-variable-substring-stability": ( + "Proposal 0047 wire-byte stability (expected_shared_prefix directive); queued for v0.13.0" + ), +} + + @pytest.mark.parametrize("fixture_path", _fixture_paths(), ids=_fixture_id) async def test_prompt_management_fixture(fixture_path: Path) -> None: + fixture_id = _fixture_id(fixture_path) + skip_if_deferred(fixture_id, _DEFERRED_FIXTURES) raw: Any = yaml.safe_load(fixture_path.read_text()) fixture = PromptManagementFixture.model_validate(raw) diff --git a/tests/test_smoke.py b/tests/test_smoke.py index 6fc5379a..a75b920e 100644 --- a/tests/test_smoke.py +++ b/tests/test_smoke.py @@ -9,7 +9,7 @@ def test_package_versions() -> None: assert openarmature.__version__ == "0.11.0" - assert openarmature.__spec_version__ == "0.38.0" + assert openarmature.__spec_version__ == "0.45.0" def test_spec_version_matches_pyproject() -> None: From 2bf45bc2f8de171497e1c65810c375e86e7310f1 Mon Sep 17 00:00:00 2001 From: chris-colinsky Date: Wed, 3 Jun 2026 16:06:55 -0700 Subject: [PATCH 2/2] Address CoPilot review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ``_deferred_cases`` in ``_run_fixture_028``: dict[str, str] → set[str]. The case-level skip uses ``continue`` (not ``pytest.skip``) so the reason values had nowhere to surface; rationale stays in the per-block comment above the set. - ``_deferral.py`` docstring: drop the awkward escaped pluralization (``\\s`` rendered as a literal ``\s``) in favor of plain prose: "it executes ``continue`` inside an async loop". --- tests/conformance/_deferral.py | 4 ++-- tests/conformance/test_observability.py | 15 +++++++-------- 2 files changed, 9 insertions(+), 10 deletions(-) diff --git a/tests/conformance/_deferral.py b/tests/conformance/_deferral.py index cd95339a..ad4dc292 100644 --- a/tests/conformance/_deferral.py +++ b/tests/conformance/_deferral.py @@ -12,8 +12,8 @@ The case-level skip inside ``_run_fixture_028`` in ``test_observability.py`` deliberately doesn't use this helper — it -``continue``\\s inside an async loop iterating sub-cases of a single -fixture rather than skipping the outer pytest test. +executes ``continue`` inside an async loop iterating sub-cases of a +single fixture rather than skipping the outer pytest test. """ from __future__ import annotations diff --git a/tests/conformance/test_observability.py b/tests/conformance/test_observability.py index db8b1bee..8080b6b6 100644 --- a/tests/conformance/test_observability.py +++ b/tests/conformance/test_observability.py @@ -895,20 +895,19 @@ async def _run_fixture_028(spec: Mapping[str, Any]) -> None: # Case-level deferrals for fixture 028. The spec extends the # fixture with new negative-control cases as new reserved keys # land in §3.4; impl-side coverage for those cases lands in the - # PR that implements the corresponding key reservation. - _deferred_cases: dict[str, str] = { + # PR that implements the corresponding key reservation. Stored as + # a set since the skip is a ``continue`` (no ``pytest.skip`` + # reason surface); rationale lives in the per-block comment above + # each name. + _deferred_cases: set[str] = { # Proposal 0052 (implementation attribution, spec v0.44.0) # extends the reserved-set 24 → 26 names with # ``implementation_name`` / ``implementation_version``. # Coverage lands in PR 3 of the v0.12.0 cycle alongside the # ``openarmature.implementation.*`` invocation-span attribute # emission. - "rejects_reserved_oa_name_implementation_name": ( - "Proposal 0052 implementation attribution; lands in PR 3 of v0.12.0" - ), - "rejects_reserved_oa_name_implementation_version": ( - "Proposal 0052 implementation attribution; lands in PR 3 of v0.12.0" - ), + "rejects_reserved_oa_name_implementation_name", + "rejects_reserved_oa_name_implementation_version", } cases = cast("list[dict[str, Any]]", spec["cases"]) for case in cases: