From 52f3c048612c3aea4cb455df86ecb32fa13dbc84 Mon Sep 17 00:00:00 2001 From: Robert Lippmann Date: Fri, 31 Jul 2026 01:31:40 -0400 Subject: [PATCH 1/3] refactor: separate grammar root exports --- src/context_compiler/__init__.py | 5 - .../conformance/api/public-api-v1.json | 91 +------------------ tests/test_api_contract_fixture.py | 3 +- tests/test_public_grammar_root_exports.py | 21 ++++- 4 files changed, 19 insertions(+), 101 deletions(-) diff --git a/src/context_compiler/__init__.py b/src/context_compiler/__init__.py index 4c63495..6333ade 100644 --- a/src/context_compiler/__init__.py +++ b/src/context_compiler/__init__.py @@ -36,7 +36,6 @@ State, create_engine, ) -from .grammar import DirectiveKind, is_canonical_directive, render_directive, validate_directive __version__ = version("context-compiler") @@ -46,7 +45,6 @@ "DECISION_CLARIFY", "DECISION_PASSTHROUGH", "DECISION_UPDATE", - "DirectiveKind", "Engine", "POLICY_PROHIBIT", "POLICY_USE", @@ -63,14 +61,11 @@ "get_preview_state_after", "get_step_decision", "get_step_state", - "is_canonical_directive", "is_clarify", "is_passthrough", "is_update", "preview", "preview_would_mutate", - "render_directive", "state_diff", "step", - "validate_directive", ] diff --git a/tests/fixtures/conformance/api/public-api-v1.json b/tests/fixtures/conformance/api/public-api-v1.json index 143079d..52bbd64 100644 --- a/tests/fixtures/conformance/api/public-api-v1.json +++ b/tests/fixtures/conformance/api/public-api-v1.json @@ -17,7 +17,6 @@ "DECISION_CLARIFY", "DECISION_PASSTHROUGH", "DECISION_UPDATE", - "DirectiveKind", "Engine", "POLICY_PROHIBIT", "POLICY_USE", @@ -34,16 +33,13 @@ "get_preview_state_after", "get_step_decision", "get_step_state", - "is_canonical_directive", "is_clarify", "is_passthrough", "is_update", "preview", "preview_would_mutate", - "render_directive", "state_diff", - "step", - "validate_directive" + "step" ], "members": { "Decision": { @@ -64,9 +60,6 @@ "kind": "constant", "value": "update" }, - "DirectiveKind": { - "kind": "class" - }, "Engine": { "kind": "class" }, @@ -265,29 +258,6 @@ ] } }, - "is_canonical_directive": { - "kind": "callable", - "signature": { - "params": [ - { - "name": "text", - "kind": "POSITIONAL_OR_KEYWORD", - "has_default": false - } - ] - }, - "shape_probes": [ - { - "kwargs": { - "text": "clear state" - }, - "return_shape": { - "type": "boolean", - "const": true - } - } - ] - }, "is_clarify": { "kind": "callable", "signature": { @@ -438,34 +408,6 @@ ] } }, - "render_directive": { - "kind": "callable", - "signature": { - "params": [ - { - "name": "kind", - "kind": "POSITIONAL_ONLY", - "has_default": false - }, - { - "name": "operands", - "kind": "VAR_KEYWORD", - "has_default": false - } - ] - }, - "shape_probes": [ - { - "args": [ - "clear_state" - ], - "return_shape": { - "type": "string", - "const": "clear state" - } - } - ] - }, "state_diff": { "kind": "callable", "signature": { @@ -560,37 +502,6 @@ } } ] - }, - "validate_directive": { - "kind": "callable", - "signature": { - "params": [ - { - "name": "text", - "kind": "POSITIONAL_OR_KEYWORD", - "has_default": false - } - ] - }, - "shape_probes": [ - { - "kwargs": { - "text": "use docker" - }, - "return_shape": { - "kind": "validated_directive", - "text": "use docker" - } - }, - { - "kwargs": { - "text": "please use docker" - }, - "return_shape": { - "type": "null" - } - } - ] } } }, diff --git a/tests/test_api_contract_fixture.py b/tests/test_api_contract_fixture.py index 4c821fc..3e4ed88 100644 --- a/tests/test_api_contract_fixture.py +++ b/tests/test_api_contract_fixture.py @@ -3,6 +3,7 @@ from pathlib import Path import context_compiler +import context_compiler.grammar as grammar _CONTRACT_PATH = ( Path(__file__).resolve().parent / "fixtures" / "conformance" / "api" / "public-api-v1.json" @@ -43,7 +44,7 @@ def _assert_shape(value: object, shape: dict[str, object], contract: dict[str, o assert actual_members == sorted(expected_members.keys()) return if "kind" in shape and shape["kind"] == "validated_directive": - assert value == context_compiler.validate_directive(shape["text"]) + assert value == grammar.validate_directive(shape["text"]) return expected_types = shape["type"] diff --git a/tests/test_public_grammar_root_exports.py b/tests/test_public_grammar_root_exports.py index 263a2e8..c75d976 100644 --- a/tests/test_public_grammar_root_exports.py +++ b/tests/test_public_grammar_root_exports.py @@ -2,11 +2,22 @@ import context_compiler.grammar as grammar_module -def test_root_reexports_read_only_grammar_contract() -> None: - assert context_compiler.DirectiveKind is grammar_module.DirectiveKind - assert context_compiler.validate_directive is grammar_module.validate_directive - assert context_compiler.render_directive is grammar_module.render_directive - assert context_compiler.is_canonical_directive is grammar_module.is_canonical_directive +def test_root_does_not_export_public_grammar_surface() -> None: + for name in ( + "DirectiveKind", + "validate_directive", + "render_directive", + "is_canonical_directive", + ): + assert name not in context_compiler.__all__ + assert not hasattr(context_compiler, name) + + +def test_grammar_submodule_preserves_public_grammar_surface() -> None: + assert grammar_module.DirectiveKind is not None + assert grammar_module.validate_directive is not None + assert grammar_module.render_directive is not None + assert grammar_module.is_canonical_directive is not None def test_root_does_not_export_private_grammar_implementation() -> None: From 3e28ef3c4f5576a1038d142be3f19c5fa77ed41d Mon Sep 17 00:00:00 2001 From: Robert Lippmann Date: Fri, 31 Jul 2026 02:03:09 -0400 Subject: [PATCH 2/3] refactor: move audit APIs behind module boundary --- README.md | 9 +- docs/api-reference.md | 17 +- examples/08_controller_preview_diff.py | 8 +- src/context_compiler/__init__.py | 16 -- src/context_compiler/audit.py | 23 ++ tests/fixtures/README.md | 18 +- .../conformance/api/public-api-v1.json | 187 ----------------- .../conformance/api/public-audit-v1.json | 198 ++++++++++++++++++ tests/test_audit_api_contract_fixture.py | 100 +++++++++ tests/test_controller.py | 10 +- tests/test_public_audit_root_exports.py | 28 +++ 11 files changed, 386 insertions(+), 228 deletions(-) create mode 100644 src/context_compiler/audit.py create mode 100644 tests/fixtures/conformance/api/public-audit-v1.json create mode 100644 tests/test_audit_api_contract_fixture.py create mode 100644 tests/test_public_audit_root_exports.py diff --git a/README.md b/README.md index a5abf26..9baae87 100644 --- a/README.md +++ b/README.md @@ -276,14 +276,15 @@ Common API entry points: - decision helpers: `is_clarify(...)`, `is_update(...)`, `is_passthrough(...)`, `get_clarify_prompt(...)`, `get_decision_state(...)` - state transport: `engine.export_json(...)`, `engine.import_json(...)` -- controller APIs: `preview(...)`, `step(...)`, `state_diff(...)` +- controller API: `step(...)` +- audit APIs: `preview(...)`, `state_diff(...)` -### Controller API (Reusable Outside REPL) +### Controller And Audit APIs (Reusable Outside REPL) -- `preview(engine, user_input)` performs a deterministic dry run and restores - live engine state afterward - `step(engine, user_input)` returns a reusable result envelope around one engine turn +- `preview(engine, user_input)` performs a deterministic dry run and restores + live engine state afterward - `state_diff(state_before, state_after)` summarizes structural state changes For examples and helper accessors such as `get_step_decision(...)`, diff --git a/docs/api-reference.md b/docs/api-reference.md index 7ca6eac..75bd343 100644 --- a/docs/api-reference.md +++ b/docs/api-reference.md @@ -189,10 +189,11 @@ Conceptual boundary: - imported policy keys are normalized during `import_json(...)` - if a policy key normalizes to `""`, the payload is invalid and is rejected -## Controller APIs +## Controller And Audit APIs -These controller APIs are public package exports and can be used directly in -host code, not only through the REPL. +The `step(...)` convenience wrapper remains part of the root host-facing API. + +Preview and structural diff helpers live under `context_compiler.audit`. ### `step(engine, user_input)` @@ -229,8 +230,8 @@ state snapshots. Typical use: ```python -from context_compiler import ( - create_engine, +from context_compiler import create_engine +from context_compiler.audit import ( diff_has_changes, get_preview_state_after, preview, @@ -246,7 +247,7 @@ if diff_has_changes(diff): show_preview(diff) ``` -Controller helper functions: +Audit helper functions: - `get_step_decision(step_result)` - `get_step_state(step_result)` @@ -255,7 +256,7 @@ Controller helper functions: - `preview_would_mutate(preview_result)` - `diff_has_changes(diff)` -For controller result-envelope details, see the controller conformance fixture +For audit result-envelope details, see the fixture documentation in [tests/fixtures/README.md](../tests/fixtures/README.md). ## Public Constants @@ -281,8 +282,6 @@ Public result and data object names exported at package root include: - `Decision` - `State` - `StepResult` -- `PreviewResult` -- `StructuralDiff` - `Engine` These names are part of the public package surface. For the exact portable API diff --git a/examples/08_controller_preview_diff.py b/examples/08_controller_preview_diff.py index 4155fe0..0242dd4 100644 --- a/examples/08_controller_preview_diff.py +++ b/examples/08_controller_preview_diff.py @@ -4,14 +4,16 @@ from context_compiler import ( create_engine, - diff_has_changes, - get_preview_decision, get_step_decision, get_step_state, + step, +) +from context_compiler.audit import ( + diff_has_changes, + get_preview_decision, preview, preview_would_mutate, state_diff, - step, ) diff --git a/src/context_compiler/__init__.py b/src/context_compiler/__init__.py index 6333ade..f9de10f 100644 --- a/src/context_compiler/__init__.py +++ b/src/context_compiler/__init__.py @@ -8,17 +8,9 @@ POLICY_USE, ) from .controller import ( - PreviewResult, StepResult, - StructuralDiff, - diff_has_changes, - get_preview_decision, - get_preview_state_after, get_step_decision, get_step_state, - preview, - preview_would_mutate, - state_diff, step, ) from .decision_helpers import ( @@ -49,23 +41,15 @@ "POLICY_PROHIBIT", "POLICY_USE", "PolicyValue", - "PreviewResult", "State", "StepResult", - "StructuralDiff", - "diff_has_changes", "create_engine", "get_clarify_prompt", "get_decision_state", - "get_preview_decision", - "get_preview_state_after", "get_step_decision", "get_step_state", "is_clarify", "is_passthrough", "is_update", - "preview", - "preview_would_mutate", - "state_diff", "step", ] diff --git a/src/context_compiler/audit.py b/src/context_compiler/audit.py new file mode 100644 index 0000000..ffada44 --- /dev/null +++ b/src/context_compiler/audit.py @@ -0,0 +1,23 @@ +"""Public auditability helpers layered above the authoritative engine.""" + +from .controller import ( + PreviewResult, + StructuralDiff, + diff_has_changes, + get_preview_decision, + get_preview_state_after, + preview, + preview_would_mutate, + state_diff, +) + +__all__ = [ + "PreviewResult", + "StructuralDiff", + "diff_has_changes", + "get_preview_decision", + "get_preview_state_after", + "preview", + "preview_would_mutate", + "state_diff", +] diff --git a/tests/fixtures/README.md b/tests/fixtures/README.md index 417435d..510702a 100644 --- a/tests/fixtures/README.md +++ b/tests/fixtures/README.md @@ -13,9 +13,11 @@ surface, rather than as Python-only test inputs. * [`engine-regression/structured/`](engine-regression/structured/) — deterministic per-turn engine regression fixtures using authoritative state snapshots. `conformance/` and `engine-regression/structured/` both cover engine behavior at different layers. -## API contract fixture +## API contract fixtures -[`conformance/api/public-api-v1.json`](conformance/api/public-api-v1.json) defines the current portable core public API contract for Python and ports. +[`conformance/api/public-api-v1.json`](conformance/api/public-api-v1.json) defines the current portable core root public API contract for Python and ports. + +[`conformance/api/public-audit-v1.json`](conformance/api/public-audit-v1.json) defines the current portable audit-module public API contract for Python and ports. Ports may sync this artifact with conformance fixtures. @@ -32,10 +34,13 @@ Ports should check equivalent public exports, members, and signatures using lang Behavioral semantics remain covered by conformance and structured fixtures. -The API contract includes the public controller helper accessors: +The root API contract includes the public step helper accessors: * `get_step_decision` * `get_step_state` + +The audit API contract includes: + * `get_preview_decision` * `get_preview_state_after` * `preview_would_mutate` @@ -88,9 +93,10 @@ Portable controller contract coverage for: * `preview(engine, user_input)` result envelope, `would_mutate`, and non-mutation of live engine state * `state_diff(state_before, state_after)` deterministic structural diff output -These fixtures keep a minimal, language-neutral contract matrix for controller APIs. -They intentionally validate the raw controller result envelopes; helper accessors -are covered separately by the public API presence contract above. +These fixtures keep a minimal, language-neutral contract matrix for step and +audit APIs. They intentionally validate the raw result envelopes; helper +accessors are covered separately by the root and audit API presence contracts +above. The current runner enforces a closed fixture shape for this family. Unknown top-level, action, expected, and documented result/diff fields are rejected. diff --git a/tests/fixtures/conformance/api/public-api-v1.json b/tests/fixtures/conformance/api/public-api-v1.json index 52bbd64..c0dcc6a 100644 --- a/tests/fixtures/conformance/api/public-api-v1.json +++ b/tests/fixtures/conformance/api/public-api-v1.json @@ -21,24 +21,16 @@ "POLICY_PROHIBIT", "POLICY_USE", "PolicyValue", - "PreviewResult", "State", "StepResult", - "StructuralDiff", - "diff_has_changes", "create_engine", "get_clarify_prompt", "get_decision_state", - "get_preview_decision", - "get_preview_state_after", "get_step_decision", "get_step_state", "is_clarify", "is_passthrough", "is_update", - "preview", - "preview_would_mutate", - "state_diff", "step" ], "members": { @@ -74,53 +66,12 @@ "PolicyValue": { "kind": "type_alias" }, - "PreviewResult": { - "kind": "type" - }, "State": { "kind": "type" }, "StepResult": { "kind": "type" }, - "StructuralDiff": { - "kind": "type" - }, - "diff_has_changes": { - "kind": "callable", - "signature": { - "params": [ - { - "name": "diff", - "kind": "POSITIONAL_OR_KEYWORD", - "has_default": false - } - ] - }, - "shape_probes": [ - { - "kwargs": { - "diff": { - "changed": true, - "premise": { - "before": null, - "after": "concise", - "changed": true - }, - "policies": { - "added": {}, - "removed": {}, - "changed": {} - } - } - }, - "return_shape": { - "type": "boolean", - "const": true - } - } - ] - }, "create_engine": { "kind": "callable", "signature": { @@ -210,30 +161,6 @@ } ] }, - "get_preview_decision": { - "kind": "callable", - "signature": { - "params": [ - { - "name": "preview_result", - "kind": "POSITIONAL_OR_KEYWORD", - "has_default": false - } - ] - } - }, - "get_preview_state_after": { - "kind": "callable", - "signature": { - "params": [ - { - "name": "preview_result", - "kind": "POSITIONAL_OR_KEYWORD", - "has_default": false - } - ] - } - }, "get_step_decision": { "kind": "callable", "signature": { @@ -343,120 +270,6 @@ } ] }, - "preview": { - "kind": "callable", - "signature": { - "params": [ - { - "name": "engine", - "kind": "POSITIONAL_OR_KEYWORD", - "has_default": false - }, - { - "name": "user_input", - "kind": "POSITIONAL_OR_KEYWORD", - "has_default": false - } - ] - }, - "shape_probes": [ - { - "kwargs": { - "engine": { - "fixture": "empty_engine" - }, - "user_input": "use docker" - }, - "return_shape": { - "type": "object", - "required_keys": [ - "output_version", - "mode", - "decision", - "state_before", - "state_after", - "diff", - "would_mutate" - ], - "properties": { - "output_version": { - "type": "number", - "const": 1 - }, - "mode": { - "type": "string", - "const": "preview" - }, - "would_mutate": { - "type": "boolean", - "const": true - } - } - } - } - ] - }, - "preview_would_mutate": { - "kind": "callable", - "signature": { - "params": [ - { - "name": "preview_result", - "kind": "POSITIONAL_OR_KEYWORD", - "has_default": false - } - ] - } - }, - "state_diff": { - "kind": "callable", - "signature": { - "params": [ - { - "name": "before", - "kind": "POSITIONAL_OR_KEYWORD", - "has_default": false - }, - { - "name": "after", - "kind": "POSITIONAL_OR_KEYWORD", - "has_default": false - } - ] - }, - "shape_probes": [ - { - "kwargs": { - "before": { - "premise": null, - "policies": {}, - "version": 2 - }, - "after": { - "premise": null, - "policies": { - "docker": "use" - }, - "version": 2 - } - }, - "return_shape": { - "type": "object", - "required_keys": [ - "changed", - "premise", - "policies" - ], - "properties": { - "changed": { - "type": "boolean", - "const": true - } - } - } - } - ] - }, "step": { "kind": "callable", "signature": { diff --git a/tests/fixtures/conformance/api/public-audit-v1.json b/tests/fixtures/conformance/api/public-audit-v1.json new file mode 100644 index 0000000..78d8163 --- /dev/null +++ b/tests/fixtures/conformance/api/public-audit-v1.json @@ -0,0 +1,198 @@ +{ + "id": "public-audit-v1", + "kind": "api-contract", + "module": "context_compiler.audit", + "exports": { + "names": [ + "PreviewResult", + "StructuralDiff", + "diff_has_changes", + "get_preview_decision", + "get_preview_state_after", + "preview", + "preview_would_mutate", + "state_diff" + ], + "members": { + "PreviewResult": { + "kind": "type" + }, + "StructuralDiff": { + "kind": "type" + }, + "diff_has_changes": { + "kind": "callable", + "signature": { + "params": [ + { + "name": "diff", + "kind": "POSITIONAL_OR_KEYWORD", + "has_default": false + } + ] + }, + "shape_probes": [ + { + "kwargs": { + "diff": { + "changed": true, + "premise": { + "before": null, + "after": "concise", + "changed": true + }, + "policies": { + "added": {}, + "removed": {}, + "changed": {} + } + } + }, + "return_shape": { + "type": "boolean", + "const": true + } + } + ] + }, + "get_preview_decision": { + "kind": "callable", + "signature": { + "params": [ + { + "name": "preview_result", + "kind": "POSITIONAL_OR_KEYWORD", + "has_default": false + } + ] + } + }, + "get_preview_state_after": { + "kind": "callable", + "signature": { + "params": [ + { + "name": "preview_result", + "kind": "POSITIONAL_OR_KEYWORD", + "has_default": false + } + ] + } + }, + "preview": { + "kind": "callable", + "signature": { + "params": [ + { + "name": "engine", + "kind": "POSITIONAL_OR_KEYWORD", + "has_default": false + }, + { + "name": "user_input", + "kind": "POSITIONAL_OR_KEYWORD", + "has_default": false + } + ] + }, + "shape_probes": [ + { + "kwargs": { + "engine": { + "fixture": "empty_engine" + }, + "user_input": "use docker" + }, + "return_shape": { + "type": "object", + "required_keys": [ + "output_version", + "mode", + "decision", + "state_before", + "state_after", + "diff", + "would_mutate" + ], + "properties": { + "output_version": { + "type": "number", + "const": 1 + }, + "mode": { + "type": "string", + "const": "preview" + }, + "would_mutate": { + "type": "boolean", + "const": true + } + } + } + } + ] + }, + "preview_would_mutate": { + "kind": "callable", + "signature": { + "params": [ + { + "name": "preview_result", + "kind": "POSITIONAL_OR_KEYWORD", + "has_default": false + } + ] + } + }, + "state_diff": { + "kind": "callable", + "signature": { + "params": [ + { + "name": "before", + "kind": "POSITIONAL_OR_KEYWORD", + "has_default": false + }, + { + "name": "after", + "kind": "POSITIONAL_OR_KEYWORD", + "has_default": false + } + ] + }, + "shape_probes": [ + { + "kwargs": { + "before": { + "premise": null, + "policies": {}, + "version": 2 + }, + "after": { + "premise": null, + "policies": { + "docker": "use" + }, + "version": 2 + } + }, + "return_shape": { + "type": "object", + "required_keys": [ + "changed", + "premise", + "policies" + ], + "properties": { + "changed": { + "type": "boolean", + "const": true + } + } + } + } + ] + } + } + } +} diff --git a/tests/test_audit_api_contract_fixture.py b/tests/test_audit_api_contract_fixture.py new file mode 100644 index 0000000..92bf254 --- /dev/null +++ b/tests/test_audit_api_contract_fixture.py @@ -0,0 +1,100 @@ +import inspect +import json +from pathlib import Path + +import context_compiler +import context_compiler.audit as audit + +_CONTRACT_PATH = ( + Path(__file__).resolve().parent / "fixtures" / "conformance" / "api" / "public-audit-v1.json" +) + + +def _load_contract() -> dict[str, object]: + return json.loads(_CONTRACT_PATH.read_text(encoding="utf-8")) + + +def _json_type_matches(value: object, expected: str) -> bool: + return { + "null": value is None, + "string": isinstance(value, str), + "object": isinstance(value, dict), + "array": isinstance(value, list), + "boolean": isinstance(value, bool), + "number": isinstance(value, int | float) and not isinstance(value, bool), + }[expected] + + +def _resolve_probe_value(value: object) -> object: + if not isinstance(value, dict) or "fixture" not in value: + return value + + fixture = value["fixture"] + if fixture == "empty_engine": + return context_compiler.create_engine() + + raise AssertionError(f"Unknown probe fixture: {fixture}") + + +def _assert_shape(value: object, shape: dict[str, object]) -> None: + expected_types = shape["type"] + if isinstance(expected_types, str): + expected_types = [expected_types] + assert any(_json_type_matches(value, expected_type) for expected_type in expected_types) + + if "const" in shape: + assert value == shape["const"] + + if isinstance(value, dict): + required_keys = shape.get("required_keys", []) + assert set(required_keys).issubset(value) + properties = shape.get("properties", {}) + for key, property_shape in properties.items(): + if key in value: + _assert_shape(value[key], property_shape) + + +def _assert_signature_matches(obj: object, expected: dict[str, object], label: str) -> None: + signature = inspect.signature(obj) + params = list(signature.parameters.values()) + expected_params = expected["params"] + + assert len(params) == len(expected_params), label + for actual, expected_param in zip(params, expected_params, strict=True): + assert actual.name == expected_param["name"], label + assert actual.kind.name == expected_param["kind"], label + assert (actual.default is not inspect.Signature.empty) is expected_param["has_default"], ( + label + ) + + +def test_public_audit_contract_matches_surface() -> None: + contract = _load_contract() + + exports = contract["exports"] + expected_names = exports["names"] + members = exports["members"] + + actual_names = sorted(name for name in audit.__all__) + assert actual_names == sorted(expected_names) + + for name in expected_names: + assert hasattr(audit, name), name + + for name, member in members.items(): + exported = getattr(audit, name) + kind = member["kind"] + if kind == "callable": + assert inspect.isroutine(exported), name + _assert_signature_matches(exported, member["signature"], name) + for probe in member.get("shape_probes", []): + args = [_resolve_probe_value(value) for value in probe.get("args", [])] + kwargs = { + key: _resolve_probe_value(value) + for key, value in probe.get("kwargs", {}).items() + } + result = exported(*args, **kwargs) + _assert_shape(result, probe["return_shape"]) + continue + assert kind == "type", name + assert inspect.isclass(exported), name diff --git a/tests/test_controller.py b/tests/test_controller.py index 6b949db..662be5b 100644 --- a/tests/test_controller.py +++ b/tests/test_controller.py @@ -5,14 +5,18 @@ from context_compiler import ( create_engine, + get_step_decision, + get_step_state, +) +from context_compiler.audit import ( diff_has_changes, get_preview_decision, get_preview_state_after, - get_step_decision, - get_step_state, + preview, preview_would_mutate, + state_diff, ) -from context_compiler.controller import preview, state_diff, step +from context_compiler.controller import step _CONTROLLER_FIXTURES_DIR = Path(__file__).resolve().parent / "fixtures" / "controller" diff --git a/tests/test_public_audit_root_exports.py b/tests/test_public_audit_root_exports.py new file mode 100644 index 0000000..6464e81 --- /dev/null +++ b/tests/test_public_audit_root_exports.py @@ -0,0 +1,28 @@ +import context_compiler +import context_compiler.audit as audit_module + + +def test_root_does_not_export_public_audit_surface() -> None: + for name in ( + "PreviewResult", + "StructuralDiff", + "diff_has_changes", + "get_preview_decision", + "get_preview_state_after", + "preview", + "preview_would_mutate", + "state_diff", + ): + assert name not in context_compiler.__all__ + assert not hasattr(context_compiler, name) + + +def test_audit_submodule_preserves_public_audit_surface() -> None: + assert audit_module.PreviewResult is not None + assert audit_module.StructuralDiff is not None + assert audit_module.diff_has_changes is not None + assert audit_module.get_preview_decision is not None + assert audit_module.get_preview_state_after is not None + assert audit_module.preview is not None + assert audit_module.preview_would_mutate is not None + assert audit_module.state_diff is not None From 338efc1632d8392ed123f64de7309893bf041010 Mon Sep 17 00:00:00 2001 From: Robert Lippmann Date: Fri, 31 Jul 2026 02:07:50 -0400 Subject: [PATCH 3/3] chore: bump dev release version --- pyproject.toml | 2 +- uv.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 0b0c36d..5f663fc 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "context-compiler" -version = "0.9.0dev1" +version = "0.9.0dev2" description = "Deterministic conversational state engine for LLM applications." readme = "README.md" requires-python = ">=3.11" diff --git a/uv.lock b/uv.lock index bfb5961..17d03c7 100644 --- a/uv.lock +++ b/uv.lock @@ -296,7 +296,7 @@ wheels = [ [[package]] name = "context-compiler" -version = "0.9.0.dev1" +version = "0.9.0.dev2" source = { editable = "." } [package.optional-dependencies]