From d0a4369fe21113e983d1f502b9622a79a9ae0c46 Mon Sep 17 00:00:00 2001 From: Robert Lippmann Date: Tue, 8 Sep 2026 09:15:53 -0400 Subject: [PATCH 1/5] refactor: rename internal state abstraction --- README.md | 8 +++++--- src/context_compiler/engine.py | 30 +++++++++++++++++------------- 2 files changed, 22 insertions(+), 16 deletions(-) diff --git a/README.md b/README.md index 85d83cd..084e74b 100644 --- a/README.md +++ b/README.md @@ -5,9 +5,11 @@ [![License](https://img.shields.io/pypi/l/context-compiler)](https://pypi.org/project/context-compiler/) [![codecov](https://codecov.io/gh/rlippmann/context-compiler/branch/main/graph/badge.svg)](https://codecov.io/gh/rlippmann/context-compiler) -Context Compiler helps LLM applications keep explicit premise and policy rules -stable across turns. It blocks invalid or conflicting changes and returns -structured decisions. +Context Compiler keeps a host-side working memory of premise and policy rules, +separate from the model's conversational context. The Engine applies +directives to that working memory so applications can carry explicit state +across model interactions without relying on the model to remember or infer +it. It blocks invalid or conflicting changes and returns structured decisions. Use it when saved context and policy rules need to shape what an application does, not just what the model sees or says. diff --git a/src/context_compiler/engine.py b/src/context_compiler/engine.py index 7a439c9..62923df 100644 --- a/src/context_compiler/engine.py +++ b/src/context_compiler/engine.py @@ -27,8 +27,8 @@ PolicyValue = Literal["use", "prohibit"] -class _State(TypedDict): - """Versioned authoritative state.""" +class _WorkingMemory(TypedDict): + """Versioned internal working memory.""" premise: str | None policies: dict[str, PolicyValue] @@ -37,7 +37,7 @@ class _State(TypedDict): class _EvaluatedTransition(TypedDict): decision: UpdateDecision | SemanticErrorDecision - next_state: _State + next_state: _WorkingMemory _NO_DIRECTIVE = NoDirectiveDecision() @@ -49,7 +49,7 @@ class Engine: __slots__ = ("_state",) def __init__(self) -> None: - self._state: _State + self._state: _WorkingMemory self._replace_state(_initial_state()) @property @@ -105,7 +105,7 @@ def apply_directive( return evaluated["decision"] def _evaluate_directive_transition( - self, state: _State, directive: CanonicalDirective + self, state: _WorkingMemory, directive: CanonicalDirective ) -> _EvaluatedTransition: error_decision = self._pre_mutation_error(directive, state=state) if error_decision is not None: @@ -117,11 +117,11 @@ def _evaluate_directive_transition( "next_state": next_state, } - def _replace_state(self, state: _State) -> None: + def _replace_state(self, state: _WorkingMemory) -> None: self._state = state def _pre_mutation_error( - self, directive: CanonicalDirective, *, state: _State | None = None + self, directive: CanonicalDirective, *, state: _WorkingMemory | None = None ) -> SemanticErrorDecision | None: candidate_state = self._state if state is None else state # Single error path: all error outcomes are detected before any mutation. @@ -203,7 +203,9 @@ def _pre_mutation_error( return None - def _apply_directive(self, directive: CanonicalDirective, *, state: _State) -> _State: + def _apply_directive( + self, directive: CanonicalDirective, *, state: _WorkingMemory + ) -> _WorkingMemory: next_state = deepcopy(state) if directive.kind is DirectiveKind.SET_PREMISE: @@ -249,7 +251,9 @@ def _apply_directive(self, directive: CanonicalDirective, *, state: _State) -> _ return _initial_state() - def _apply_replacement_explicit(self, state: _State, new_item: str, old_item: str) -> None: + def _apply_replacement_explicit( + self, state: _WorkingMemory, new_item: str, old_item: str + ) -> None: new_key = _normalize_item(new_item) old_key = _normalize_item(old_item) @@ -260,7 +264,7 @@ def _apply_replacement_explicit(self, state: _State, new_item: str, old_item: st state[STATE_POLICIES][new_key] = POLICY_USE -def _initial_state() -> _State: +def _initial_state() -> _WorkingMemory: return { STATE_PREMISE: None, STATE_POLICIES: {}, @@ -268,7 +272,7 @@ def _initial_state() -> _State: } -def _load_state_json(payload: str) -> _State: +def _load_state_json(payload: str) -> _WorkingMemory: try: raw = json.loads(payload) except json.JSONDecodeError as exc: @@ -277,7 +281,7 @@ def _load_state_json(payload: str) -> _State: return _load_state_obj(raw) -def _load_state_obj(raw: object) -> _State: +def _load_state_obj(raw: object) -> _WorkingMemory: if not isinstance(raw, dict): raise ValueError("Invalid state payload.") @@ -379,5 +383,5 @@ def _repair_set_premise(value: str) -> CanonicalDirective: ) -def _update_decision(previous_state: _State, next_state: _State) -> UpdateDecision: +def _update_decision(previous_state: _WorkingMemory, next_state: _WorkingMemory) -> UpdateDecision: return UpdateDecision(changed=previous_state != next_state) From beeff6f5777af1e27d9578611ed47ef4d77df418 Mon Sep 17 00:00:00 2001 From: Robert Lippmann Date: Tue, 8 Sep 2026 09:19:52 -0400 Subject: [PATCH 2/5] docs: clarify working memory terminology --- README.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README.md b/README.md index 084e74b..5c7511b 100644 --- a/README.md +++ b/README.md @@ -5,11 +5,11 @@ [![License](https://img.shields.io/pypi/l/context-compiler)](https://pypi.org/project/context-compiler/) [![codecov](https://codecov.io/gh/rlippmann/context-compiler/branch/main/graph/badge.svg)](https://codecov.io/gh/rlippmann/context-compiler) -Context Compiler keeps a host-side working memory of premise and policy rules, -separate from the model's conversational context. The Engine applies -directives to that working memory so applications can carry explicit state -across model interactions without relying on the model to remember or infer -it. It blocks invalid or conflicting changes and returns structured decisions. +Context Compiler keeps host-side working memory consisting of a premise and +policies, separate from the model's conversational context. The Engine applies +directives to that working memory so applications can maintain explicit state +across model interactions without relying on the model to remember or infer it. +It blocks invalid or conflicting changes and returns structured decisions. Use it when saved context and policy rules need to shape what an application does, not just what the model sees or says. @@ -92,7 +92,7 @@ repair behavior. ## State Model -The engine stores explicit user commitments as saved state: +Working memory contains explicit user commitments: | State | Meaning | | --- | --- | From ddc0735b12b871960e5ed6b6d3315e0a1cdbc1c5 Mon Sep 17 00:00:00 2001 From: Robert Lippmann Date: Tue, 8 Sep 2026 09:22:49 -0400 Subject: [PATCH 3/5] docs: align working memory wording --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 5c7511b..6534b98 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [![License](https://img.shields.io/pypi/l/context-compiler)](https://pypi.org/project/context-compiler/) [![codecov](https://codecov.io/gh/rlippmann/context-compiler/branch/main/graph/badge.svg)](https://codecov.io/gh/rlippmann/context-compiler) -Context Compiler keeps host-side working memory consisting of a premise and +Context Compiler maintains host-side working memory consisting of a premise and policies, separate from the model's conversational context. The Engine applies directives to that working memory so applications can maintain explicit state across model interactions without relying on the model to remember or infer it. @@ -122,8 +122,8 @@ To replace an existing `use` policy: use podman instead of docker ``` -If `docker` is absent from saved state, the replacement fails and leaves state -unchanged. It does not become plain `use podman`. +If `docker` is absent from working memory, the replacement fails and leaves +working memory unchanged. It does not become plain `use podman`. To remove a policy or clear state: From ed3449ec7742db19709e4abbfae038c953ab6654 Mon Sep 17 00:00:00 2001 From: Robert Lippmann Date: Mon, 14 Sep 2026 02:31:38 -0400 Subject: [PATCH 4/5] refactor: rename engine working memory attribute --- src/context_compiler/engine.py | 16 ++++++++-------- tests/test_engine.py | 2 +- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/context_compiler/engine.py b/src/context_compiler/engine.py index 62923df..d44bc6c 100644 --- a/src/context_compiler/engine.py +++ b/src/context_compiler/engine.py @@ -46,28 +46,28 @@ class _EvaluatedTransition(TypedDict): class Engine: """Own the authoritative state and apply one directive transition at a time.""" - __slots__ = ("_state",) + __slots__ = ("_working_memory",) def __init__(self) -> None: - self._state: _WorkingMemory + self._working_memory: _WorkingMemory self._replace_state(_initial_state()) @property def premise(self) -> str | None: """Return the current premise from authoritative state.""" - return self._state[STATE_PREMISE] + return self._working_memory[STATE_PREMISE] @property def policies(self) -> Mapping[str, PolicyValue]: """Return a defensive copy of the current policy mapping.""" - return deepcopy(self._state[STATE_POLICIES]) + return deepcopy(self._working_memory[STATE_POLICIES]) def export_json(self) -> str: """Serialize the current authoritative state to canonical JSON text.""" - return json.dumps(self._state, sort_keys=True, separators=(",", ":")) + return json.dumps(self._working_memory, sort_keys=True, separators=(",", ":")) def import_json(self, payload: str) -> None: """Replace authoritative state from previously exported JSON text. @@ -100,7 +100,7 @@ def apply_directive( ) -> UpdateDecision | SemanticErrorDecision: """Evaluate and commit one canonical directive against authoritative state.""" - evaluated = self._evaluate_directive_transition(self._state, directive) + evaluated = self._evaluate_directive_transition(self._working_memory, directive) self._replace_state(evaluated["next_state"]) return evaluated["decision"] @@ -118,12 +118,12 @@ def _evaluate_directive_transition( } def _replace_state(self, state: _WorkingMemory) -> None: - self._state = state + self._working_memory = state def _pre_mutation_error( self, directive: CanonicalDirective, *, state: _WorkingMemory | None = None ) -> SemanticErrorDecision | None: - candidate_state = self._state if state is None else state + candidate_state = self._working_memory if state is None else state # Single error path: all error outcomes are detected before any mutation. if ( directive.kind is DirectiveKind.SET_PREMISE diff --git a/tests/test_engine.py b/tests/test_engine.py index b48cc62..4cb6664 100644 --- a/tests/test_engine.py +++ b/tests/test_engine.py @@ -375,7 +375,7 @@ def test_import_json_rejects_policy_keys_that_normalize_to_empty( def test_replace_use_errors_when_old_policy_is_not_use_in_invalid_internal_state() -> None: engine = Engine() # Defensive-path coverage for impossible external state values. - engine._state["policies"]["docker"] = "invalid" # type: ignore[assignment] # noqa: SLF001 + engine._working_memory["policies"]["docker"] = "invalid" # type: ignore[assignment] # noqa: SLF001 decision = engine.step("use kubectl instead of docker") From c4ef18b1873a305c71dc8e10700eff3e406a699f Mon Sep 17 00:00:00 2001 From: Robert Lippmann Date: Mon, 14 Sep 2026 02:36:44 -0400 Subject: [PATCH 5/5] chore: bump patch version --- pyproject.toml | 2 +- uv.lock | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index b665802..66aad42 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "hatchling.build" [project] name = "context-compiler" -version = "0.9.1" +version = "0.9.2" description = "Deterministic conversational state engine for LLM applications." readme = "README.md" requires-python = ">=3.11" diff --git a/uv.lock b/uv.lock index 8211ce8..7882b6b 100644 --- a/uv.lock +++ b/uv.lock @@ -296,7 +296,7 @@ wheels = [ [[package]] name = "context-compiler" -version = "0.9.1" +version = "0.9.2" source = { editable = "." } [package.optional-dependencies]