From 9e8d2715bae34bc36e845e1e609c6c9f9ab6b3a2 Mon Sep 17 00:00:00 2001 From: Robert Lippmann Date: Mon, 11 May 2026 01:48:03 -0400 Subject: [PATCH 1/2] test: add fixture-backed api surface contract checks --- tests/fixtures/README.md | 11 +++++++ .../conformance/api/public-api-v1.json | 29 ++++++++++++++++ .../fixtures/preprocessor/public-api-v1.json | 18 ++++++++++ tests/test_api_contract_fixture.py | 33 +++++++++++++++++++ .../test_preprocessor_api_contract_fixture.py | 29 ++++++++++++++++ tests/test_preprocessor_conformance.py | 6 +++- 6 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 tests/fixtures/conformance/api/public-api-v1.json create mode 100644 tests/fixtures/preprocessor/public-api-v1.json create mode 100644 tests/test_api_contract_fixture.py create mode 100644 tests/test_preprocessor_api_contract_fixture.py diff --git a/tests/fixtures/README.md b/tests/fixtures/README.md index 96e5261..883eca2 100644 --- a/tests/fixtures/README.md +++ b/tests/fixtures/README.md @@ -5,11 +5,22 @@ This directory contains multiple fixture suites with different contracts. ## Fixture types * [`conformance/`](conformance/) — core engine cross-language conformance contract. + Includes a small public API presence contract under `conformance/api/`. * [`engine-regression/structured/`](engine-regression/structured/) — deterministic per-turn engine regression fixtures (including checkpoint snapshots). * [`preprocessor/`](preprocessor/) — preprocessor heuristic and validation fixtures. `conformance/` and `engine-regression/structured/` both cover engine behavior at different layers; preprocessor fixtures are intentionally separate from the core engine conformance contract. +## API contract fixture + +[`conformance/api/public-api-v1.json`](conformance/api/public-api-v1.json) defines a small public API presence contract for the Python 0.6 surface that ports must expose. + +Ports may sync this artifact with conformance fixtures. + +Ports should check equivalent public exports and methods using language-appropriate names where casing differs. + +Behavioral semantics remain covered by conformance and structured fixtures. + ## Step fixtures For [`conformance/step/`](conformance/step/): diff --git a/tests/fixtures/conformance/api/public-api-v1.json b/tests/fixtures/conformance/api/public-api-v1.json new file mode 100644 index 0000000..921e68c --- /dev/null +++ b/tests/fixtures/conformance/api/public-api-v1.json @@ -0,0 +1,29 @@ +{ + "id": "public-api-v1", + "kind": "api-contract", + "target": "context-compiler-ports", + "module": "context_compiler", + "required_exports": [ + "create_engine", + "compile_transcript", + "get_premise_value", + "get_policy_items", + "TranscriptMessage", + "Transcript", + "ApplyResult" + ], + "engine": { + "type": "Engine", + "required_members": [ + "step", + "state", + "export_json", + "import_json", + "apply_transcript", + "export_checkpoint", + "import_checkpoint", + "export_checkpoint_json", + "import_checkpoint_json" + ] + } +} diff --git a/tests/fixtures/preprocessor/public-api-v1.json b/tests/fixtures/preprocessor/public-api-v1.json new file mode 100644 index 0000000..c314bdb --- /dev/null +++ b/tests/fixtures/preprocessor/public-api-v1.json @@ -0,0 +1,18 @@ +{ + "id": "experimental-preprocessor-public-api-v1", + "kind": "api-contract", + "module": "experimental.preprocessor", + "required_exports": [ + "PRECOMPILE_OUTCOME_DIRECTIVE", + "PRECOMPILE_OUTCOME_NO_DIRECTIVE", + "PRECOMPILE_OUTCOME_UNKNOWN", + "PRECOMPILER_NO_DIRECTIVE_SENTINEL", + "PrecompileResult", + "PrecompileOutcome", + "parse_preprocessor_output", + "parse_precompiler_output", + "precompile_heuristic", + "render_prompt", + "validate_precompiler_output" + ] +} diff --git a/tests/test_api_contract_fixture.py b/tests/test_api_contract_fixture.py new file mode 100644 index 0000000..a16afb1 --- /dev/null +++ b/tests/test_api_contract_fixture.py @@ -0,0 +1,33 @@ +import json +from pathlib import Path + +import context_compiler + +_CONTRACT_PATH = ( + Path(__file__).resolve().parent / "fixtures" / "conformance" / "api" / "public-api-v1.json" +) + + +def _load_contract() -> dict[str, object]: + return json.loads(_CONTRACT_PATH.read_text(encoding="utf-8")) + + +def test_api_contract_fixture_matches_python_public_surface() -> None: + contract = _load_contract() + + assert contract["kind"] == "api-contract" + for name in contract["required_exports"]: + assert hasattr(context_compiler, name), name + assert name in context_compiler.__all__, name + + engine = context_compiler.create_engine() + for name in contract["engine"]["required_members"]: + assert hasattr(engine, name), name + + +def test_api_contract_fixture_has_unique_entries() -> None: + contract = _load_contract() + required_exports = contract["required_exports"] + assert len(required_exports) == len(set(required_exports)) + required_members = contract["engine"]["required_members"] + assert len(required_members) == len(set(required_members)) diff --git a/tests/test_preprocessor_api_contract_fixture.py b/tests/test_preprocessor_api_contract_fixture.py new file mode 100644 index 0000000..61a5c78 --- /dev/null +++ b/tests/test_preprocessor_api_contract_fixture.py @@ -0,0 +1,29 @@ +import json +from pathlib import Path + +import experimental.preprocessor as preprocessor + +_CONTRACT_PATH = ( + Path(__file__).resolve().parent / "fixtures" / "preprocessor" / "public-api-v1.json" +) + + +def _load_contract() -> dict[str, object]: + return json.loads(_CONTRACT_PATH.read_text(encoding="utf-8")) + + +def test_preprocessor_api_contract_fixture_matches_public_surface() -> None: + contract = _load_contract() + + assert contract["kind"] == "api-contract" + + for name in contract["required_exports"]: + assert hasattr(preprocessor, name), name + assert name in preprocessor.__all__, name + + +def test_preprocessor_api_contract_fixture_has_unique_entries() -> None: + contract = _load_contract() + + required_exports = contract["required_exports"] + assert len(required_exports) == len(set(required_exports)) diff --git a/tests/test_preprocessor_conformance.py b/tests/test_preprocessor_conformance.py index 6b72c15..61d1c9e 100644 --- a/tests/test_preprocessor_conformance.py +++ b/tests/test_preprocessor_conformance.py @@ -9,7 +9,11 @@ def _fixture_paths() -> list[Path]: - return sorted(_PREPROCESSOR_FIXTURES_DIR.glob("*.json")) + return sorted( + path + for path in _PREPROCESSOR_FIXTURES_DIR.glob("*.json") + if not path.name.startswith("public-api-") + ) def _load_fixture(path: Path) -> dict[str, object]: From 2dc2b02e8ee4caf0aa702193b5da08260278867c Mon Sep 17 00:00:00 2001 From: Robert Lippmann Date: Mon, 11 May 2026 01:49:46 -0400 Subject: [PATCH 2/2] chore: bump version to 0.6.17 --- pyproject.toml | 2 +- uv.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 2bed5ba..e2af2b3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "context-compiler" -version = "0.6.16" +version = "0.6.17" description = "Deterministic conversational state engine for LLM applications." readme = "README.md" requires-python = ">=3.11" diff --git a/uv.lock b/uv.lock index bfdfa14..7cd06ab 100644 --- a/uv.lock +++ b/uv.lock @@ -468,7 +468,7 @@ wheels = [ [[package]] name = "context-compiler" -version = "0.6.16" +version = "0.6.17" source = { editable = "." } [package.optional-dependencies]