context-compiler 0.9.0.dev3__tar.gz → 0.9.0.dev5__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (42) hide show
  1. {context_compiler-0.9.0.dev3 → context_compiler-0.9.0.dev5}/PKG-INFO +46 -58
  2. {context_compiler-0.9.0.dev3 → context_compiler-0.9.0.dev5}/README.md +45 -49
  3. context_compiler-0.9.0.dev3/demos/01_llm_contradiction_clarify.py → context_compiler-0.9.0.dev5/demos/01_llm_contradiction_error.py +35 -24
  4. {context_compiler-0.9.0.dev3 → context_compiler-0.9.0.dev5}/demos/02_llm_constraint_guardrail.py +15 -7
  5. {context_compiler-0.9.0.dev3 → context_compiler-0.9.0.dev5}/demos/03_llm_premise_guardrail.py +22 -4
  6. {context_compiler-0.9.0.dev3 → context_compiler-0.9.0.dev5}/demos/04_llm_tool_denylist_guardrail.py +15 -7
  7. {context_compiler-0.9.0.dev3 → context_compiler-0.9.0.dev5}/demos/05_llm_prompt_drift_vs_state.py +19 -7
  8. {context_compiler-0.9.0.dev3 → context_compiler-0.9.0.dev5}/demos/06_llm_context_compaction.py +5 -4
  9. {context_compiler-0.9.0.dev3 → context_compiler-0.9.0.dev5}/demos/07_llm_prompt_vs_state.py +38 -10
  10. {context_compiler-0.9.0.dev3 → context_compiler-0.9.0.dev5}/demos/08_llm_replacement_precondition.py +22 -12
  11. context_compiler-0.9.0.dev3/demos/09_llm_confirmation_passthrough.py → context_compiler-0.9.0.dev5/demos/09_llm_confirmation_no_directive.py +39 -30
  12. {context_compiler-0.9.0.dev3 → context_compiler-0.9.0.dev5}/demos/README.md +2 -2
  13. {context_compiler-0.9.0.dev3 → context_compiler-0.9.0.dev5}/demos/common.py +72 -45
  14. {context_compiler-0.9.0.dev3 → context_compiler-0.9.0.dev5}/demos/run_demo.py +2 -2
  15. {context_compiler-0.9.0.dev3 → context_compiler-0.9.0.dev5}/examples/01_persistent_guardrails.py +11 -3
  16. {context_compiler-0.9.0.dev3 → context_compiler-0.9.0.dev5}/examples/02_configuration_and_correction.py +7 -3
  17. context_compiler-0.9.0.dev3/examples/03_ambiguity_with_clarification.py → context_compiler-0.9.0.dev5/examples/03_ambiguity_with_error.py +11 -7
  18. {context_compiler-0.9.0.dev3 → context_compiler-0.9.0.dev5}/examples/04_tool_governance_denylist.py +6 -3
  19. {context_compiler-0.9.0.dev3 → context_compiler-0.9.0.dev5}/examples/05_llm_integration_pattern.py +17 -14
  20. {context_compiler-0.9.0.dev3 → context_compiler-0.9.0.dev5}/examples/06_step_sequence_and_state_restore.py +9 -3
  21. {context_compiler-0.9.0.dev3 → context_compiler-0.9.0.dev5}/examples/07_single_policy_correction.py +6 -2
  22. {context_compiler-0.9.0.dev3 → context_compiler-0.9.0.dev5}/examples/README.md +4 -11
  23. context_compiler-0.9.0.dev5/examples/_util.py +71 -0
  24. {context_compiler-0.9.0.dev3 → context_compiler-0.9.0.dev5}/pyproject.toml +10 -1
  25. {context_compiler-0.9.0.dev3 → context_compiler-0.9.0.dev5}/src/context_compiler/__init__.py +10 -24
  26. {context_compiler-0.9.0.dev3 → context_compiler-0.9.0.dev5}/src/context_compiler/const.py +2 -2
  27. context_compiler-0.9.0.dev5/src/context_compiler/decision_helpers.py +30 -0
  28. {context_compiler-0.9.0.dev3 → context_compiler-0.9.0.dev5}/src/context_compiler/engine.py +115 -86
  29. {context_compiler-0.9.0.dev3 → context_compiler-0.9.0.dev5}/src/context_compiler/grammar.py +54 -13
  30. {context_compiler-0.9.0.dev3 → context_compiler-0.9.0.dev5}/src/context_compiler/repl.py +121 -150
  31. context_compiler-0.9.0.dev3/examples/08_controller_preview_diff.py +0 -42
  32. context_compiler-0.9.0.dev3/examples/_util.py +0 -56
  33. context_compiler-0.9.0.dev3/src/context_compiler/audit.py +0 -23
  34. context_compiler-0.9.0.dev3/src/context_compiler/controller.py +0 -149
  35. context_compiler-0.9.0.dev3/src/context_compiler/decision_helpers.py +0 -24
  36. {context_compiler-0.9.0.dev3 → context_compiler-0.9.0.dev5}/.gitignore +0 -0
  37. {context_compiler-0.9.0.dev3 → context_compiler-0.9.0.dev5}/LICENSE +0 -0
  38. {context_compiler-0.9.0.dev3 → context_compiler-0.9.0.dev5}/demos/__init__.py +0 -0
  39. {context_compiler-0.9.0.dev3 → context_compiler-0.9.0.dev5}/demos/llm_client.py +0 -0
  40. {context_compiler-0.9.0.dev3 → context_compiler-0.9.0.dev5}/host_support/__init__.py +0 -0
  41. {context_compiler-0.9.0.dev3 → context_compiler-0.9.0.dev5}/host_support/provider_mode.py +0 -0
  42. {context_compiler-0.9.0.dev3 → context_compiler-0.9.0.dev5}/src/context_compiler/py.typed +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: context-compiler
3
- Version: 0.9.0.dev3
3
+ Version: 0.9.0.dev5
4
4
  Summary: Deterministic conversational state engine for LLM applications.
5
5
  Project-URL: Homepage, https://github.com/rlippmann/context-compiler
6
6
  Project-URL: Repository, https://github.com/rlippmann/context-compiler
@@ -18,14 +18,6 @@ Classifier: Topic :: Software Development :: Libraries
18
18
  Requires-Python: >=3.11
19
19
  Provides-Extra: demos
20
20
  Requires-Dist: litellm>=1.0.0; extra == 'demos'
21
- Provides-Extra: dev
22
- Requires-Dist: coverage; extra == 'dev'
23
- Requires-Dist: hypothesis; extra == 'dev'
24
- Requires-Dist: mypy<2.0,>=1.12; extra == 'dev'
25
- Requires-Dist: pre-commit; extra == 'dev'
26
- Requires-Dist: pytest; extra == 'dev'
27
- Requires-Dist: pytest-cov; extra == 'dev'
28
- Requires-Dist: ruff<1.0,>=0.12; extra == 'dev'
29
21
  Description-Content-Type: text/markdown
30
22
 
31
23
  # Context Compiler
@@ -36,7 +28,7 @@ Description-Content-Type: text/markdown
36
28
 
37
29
  Context Compiler is a deterministic conversational state authority for LLM applications.
38
30
  It handles canonical directive execution, semantic validation, deterministic
39
- clarify decisions, runtime semantic continuation boundaries, and structured authoritative state for
31
+ error decisions, runtime semantic continuation boundaries, and structured authoritative state for
40
32
  the host.
41
33
 
42
34
  ## What Context Compiler provides
@@ -44,7 +36,7 @@ the host.
44
36
  Context Compiler gives hosts fixed state rules:
45
37
 
46
38
  - handle canonical explicit state changes with deterministic rules
47
- - clarification instead of silent overwrite for blocked/ambiguous changes
39
+ - error instead of silent overwrite for blocked/ambiguous changes
48
40
  - preserve supported pending continuation when explicit confirmation is required
49
41
  - export and import authoritative state for host-managed persistence
50
42
  - produce structured authoritative state for downstream host decisions
@@ -89,7 +81,7 @@ Context Compiler makes state-change rules explicit so behavior stays repeatable.
89
81
  The architecture has three layers:
90
82
 
91
83
  - syntax classification decides whether input is a canonical directive, invalid
92
- directive-shaped syntax, or ordinary passthrough
84
+ directive-shaped syntax, or ordinary no_directive
93
85
  - semantic evaluation decides whether a canonical directive updates state,
94
86
  clarifies, or no-ops
95
87
  - semantic continuation optionally preserves a deterministic blocked transition
@@ -123,7 +115,7 @@ use podman instead of docker
123
115
  - Without explicit state transition rules: behavior depends on host/model handling
124
116
  - Context Compiler: applies the deterministic resulting transition when
125
117
  `docker` is absent and `use podman` is otherwise valid; other semantic
126
- conflicts may still clarify
118
+ conflicts may still error
127
119
 
128
120
  ### Lifecycle enforcement
129
121
 
@@ -133,7 +125,7 @@ change premise to formal tone
133
125
  ```
134
126
 
135
127
  - Without explicit transition checks: behavior depends on host/model handling
136
- - Context Compiler: asks for clarification and keeps saved state unchanged
128
+ - Context Compiler: asks for error and keeps saved state unchanged
137
129
 
138
130
  ---
139
131
 
@@ -150,9 +142,9 @@ Decision
150
142
 
151
143
 
152
144
  Host Application
153
- ├─ clarify → ask user
154
- ├─ passthroughcall LLM
155
- └─ update → authoritative state mutated; host may call LLM with compiled state
145
+ ├─ error → ask user
146
+ ├─ no_directiveno canonical directive recognized; host decides what to do next
147
+ └─ update → authoritative state mutated; host may use compiled state downstream
156
148
  ```
157
149
 
158
150
  The compiler never calls the LLM. Your app decides what to do with the returned
@@ -167,8 +159,7 @@ Use Context Compiler in your host application first:
167
159
  ```python
168
160
  from context_compiler import (
169
161
  create_engine,
170
- get_clarify_prompt,
171
- is_clarify,
162
+ is_error,
172
163
  is_update,
173
164
  )
174
165
 
@@ -177,10 +168,14 @@ engine = create_engine()
177
168
  user_input = "set premise current project uses uv"
178
169
  decision = engine.step(user_input)
179
170
 
180
- if is_clarify(decision):
181
- show_to_user(get_clarify_prompt(decision))
171
+ if is_error(decision):
172
+ show_to_user(decision["message"])
182
173
  elif is_update(decision):
183
- messages = build_messages(engine.state, user_input)
174
+ messages = build_messages(
175
+ premise=engine.premise,
176
+ policies=engine.policies,
177
+ user_input=user_input,
178
+ )
184
179
  render(call_llm(messages))
185
180
  else:
186
181
  render(call_llm(user_input))
@@ -224,10 +219,9 @@ Preload options load authoritative state:
224
219
  - `--initial-state-json` / `--initial-state-file` load saved state
225
220
  (via exported state JSON).
226
221
 
227
- REPL commands (controller layer, not engine directives):
222
+ REPL commands (not engine directives):
228
223
 
229
224
  - `state` shows current saved state.
230
- - `preview <input>` runs deterministic dry-run without mutating live state.
231
225
  - `step <input>` is an explicit alias of normal bare-input step behavior.
232
226
 
233
227
  Bare REPL input behavior remains unchanged.
@@ -268,7 +262,7 @@ Packaging notes:
268
262
  ### Development
269
263
 
270
264
  ```bash
271
- uv sync --group dev
265
+ uv sync --dev
272
266
  uv run pytest
273
267
  ```
274
268
 
@@ -277,22 +271,30 @@ uv run pytest
277
271
  Each user message produces a `Decision`.
278
272
 
279
273
  ```python
274
+ class DecisionKind(StrEnum):
275
+ NO_DIRECTIVE = "no_directive"
276
+ UPDATE = "update"
277
+ ERROR = "error"
278
+
280
279
  class Decision(TypedDict):
281
- kind: Literal["passthrough", "update", "clarify"]
282
- state: dict | None
283
- prompt_to_user: str | None
280
+ kind: DecisionKind
281
+ message: str | None
284
282
  ```
285
283
 
284
+ `message` is structurally present on every `Decision`, but it is semantically
285
+ meaningful only for `error`. For `no_directive` and `update`, `message` is
286
+ `None`.
287
+
286
288
  Meaning:
287
289
 
288
290
  | kind | host behavior |
289
291
  | --- | --- |
290
- | passthrough | forward user input to LLM |
291
- | update | authoritative state mutated; host may call LLM with updated state |
292
- | clarify | show `prompt_to_user` and do not call the LLM |
292
+ | no_directive | no canonical directive recognized; no authoritative state change; host decides what to do next |
293
+ | update | authoritative state mutated; host may use updated state downstream |
294
+ | error | show `message` and do not continue normal downstream processing yet |
293
295
 
294
- For normal app code, prefer the exported decision helpers (`is_clarify`,
295
- `is_update`, `is_passthrough`, `get_clarify_prompt`, `get_decision_state`)
296
+ For normal app code, prefer the exported decision helpers (`is_error`,
297
+ `is_update`, `is_no_directive`, `get_error_message`)
296
298
  instead of direct key traversal.
297
299
 
298
300
  See [docs/api-reference.md](docs/api-reference.md) for the full public API
@@ -300,26 +302,12 @@ reference.
300
302
 
301
303
  Common API entry points:
302
304
 
303
- - engine lifecycle: `create_engine(...)`, `engine.step(...)`, `engine.state`,
305
+ - engine lifecycle: `create_engine()`, `engine.step(...)`,
304
306
  `engine.premise`, `engine.policies`, `engine.export_json(...)`,
305
307
  `engine.import_json(...)`
306
- - decision helpers: `is_clarify(...)`, `is_update(...)`, `is_passthrough(...)`,
307
- `get_clarify_prompt(...)`, `get_decision_state(...)`
308
+ - decision helpers: `is_error(...)`, `is_update(...)`, `is_no_directive(...)`,
309
+ `get_error_message(...)`
308
310
  - state transport: `engine.export_json(...)`, `engine.import_json(...)`
309
- - controller API: `step(...)`
310
- - audit APIs: `preview(...)`, `state_diff(...)`
311
-
312
- ### Controller And Audit APIs (Reusable Outside REPL)
313
-
314
- - `step(engine, user_input)` returns a reusable result envelope around one
315
- engine turn
316
- - `preview(engine, user_input)` performs a deterministic dry run and restores
317
- live engine state afterward
318
- - `state_diff(state_before, state_after)` summarizes structural state changes
319
-
320
- For examples and helper accessors such as `get_step_decision(...)`,
321
- `get_preview_state_after(...)`, `preview_would_mutate(...)`, and
322
- `diff_has_changes(...)`, see [docs/api-reference.md](docs/api-reference.md).
323
311
 
324
312
  ---
325
313
 
@@ -343,8 +331,8 @@ Identical input sequences always produce identical state.
343
331
  For live engine-owned reads, use `engine.premise` and `engine.policies`.
344
332
  `engine.policies` returns a caller-owned copy.
345
333
 
346
- `engine.state` remains the public snapshot/serialization boundary when host code
347
- needs the full authoritative state object.
334
+ Use `engine.export_json()` and `engine.import_json()` for persistence and
335
+ restoration.
348
336
 
349
337
  ---
350
338
 
@@ -441,14 +429,14 @@ User: clear state
441
429
 
442
430
  Grammar invariant: one input may contain at most one canonical directive.
443
431
  Directive-shaped invalid input is outside the canonical language, and
444
- `clarify` is reserved for canonical directives that later fail semantic
432
+ `error` is reserved for canonical directives that later fail semantic
445
433
  evaluation against authoritative state.
446
434
 
447
435
  Pending continuation is a separate runtime layer. It may exist only after a
448
- canonical directive reaches a supported semantic `clarify` case. It never
436
+ canonical directive reaches a supported semantic `error` case. It never
449
437
  repairs malformed syntax or reinterprets non-canonical input as a directive.
450
438
  An absent source item in a canonical replacement directive is not, by itself,
451
- such a `clarify` case.
439
+ such a `error` case.
452
440
 
453
441
  Examples:
454
442
 
@@ -496,7 +484,7 @@ boundary, see [DirectiveGrammarSpec.md](docs/DirectiveGrammarSpec.md).
496
484
  **Isn't this just prompt reinjection?**
497
485
  No. Prompt construction is one downstream use of authoritative state.
498
486
  Context Compiler is the authority layer that decides when state changes are
499
- allowed, when clarification is required, and how continuation state is
487
+ allowed, when error is required, and how continuation state is
500
488
  restored. For runnable application-layer examples, see
501
489
  [`context-compiler-example-integrations`](https://github.com/rlippmann/context-compiler-example-integrations).
502
490
 
@@ -519,7 +507,7 @@ User: prohibit python_script
519
507
 
520
508
  Without an authority layer, the application must invent conflict-resolution and
521
509
  continuation rules itself. Context Compiler applies deterministic
522
- state-transition rules and can return clarification instead of silently
510
+ state-transition rules and can return error instead of silently
523
511
  overwriting state.
524
512
 
525
513
  ---
@@ -531,7 +519,7 @@ overwriting state.
531
519
  - State changes only through explicit user directives or confirmation.
532
520
  - Identical input sequences produce identical compiler state.
533
521
  - Model responses never modify compiler state.
534
- - Ambiguous directives trigger clarification instead of changing state.
522
+ - Ambiguous directives trigger error instead of changing state.
535
523
  - Syntax errors never create pending continuation.
536
524
 
537
525
  Behavioral tests and Hypothesis-based property tests verify these invariants.
@@ -6,7 +6,7 @@
6
6
 
7
7
  Context Compiler is a deterministic conversational state authority for LLM applications.
8
8
  It handles canonical directive execution, semantic validation, deterministic
9
- clarify decisions, runtime semantic continuation boundaries, and structured authoritative state for
9
+ error decisions, runtime semantic continuation boundaries, and structured authoritative state for
10
10
  the host.
11
11
 
12
12
  ## What Context Compiler provides
@@ -14,7 +14,7 @@ the host.
14
14
  Context Compiler gives hosts fixed state rules:
15
15
 
16
16
  - handle canonical explicit state changes with deterministic rules
17
- - clarification instead of silent overwrite for blocked/ambiguous changes
17
+ - error instead of silent overwrite for blocked/ambiguous changes
18
18
  - preserve supported pending continuation when explicit confirmation is required
19
19
  - export and import authoritative state for host-managed persistence
20
20
  - produce structured authoritative state for downstream host decisions
@@ -59,7 +59,7 @@ Context Compiler makes state-change rules explicit so behavior stays repeatable.
59
59
  The architecture has three layers:
60
60
 
61
61
  - syntax classification decides whether input is a canonical directive, invalid
62
- directive-shaped syntax, or ordinary passthrough
62
+ directive-shaped syntax, or ordinary no_directive
63
63
  - semantic evaluation decides whether a canonical directive updates state,
64
64
  clarifies, or no-ops
65
65
  - semantic continuation optionally preserves a deterministic blocked transition
@@ -93,7 +93,7 @@ use podman instead of docker
93
93
  - Without explicit state transition rules: behavior depends on host/model handling
94
94
  - Context Compiler: applies the deterministic resulting transition when
95
95
  `docker` is absent and `use podman` is otherwise valid; other semantic
96
- conflicts may still clarify
96
+ conflicts may still error
97
97
 
98
98
  ### Lifecycle enforcement
99
99
 
@@ -103,7 +103,7 @@ change premise to formal tone
103
103
  ```
104
104
 
105
105
  - Without explicit transition checks: behavior depends on host/model handling
106
- - Context Compiler: asks for clarification and keeps saved state unchanged
106
+ - Context Compiler: asks for error and keeps saved state unchanged
107
107
 
108
108
  ---
109
109
 
@@ -120,9 +120,9 @@ Decision
120
120
 
121
121
 
122
122
  Host Application
123
- ├─ clarify → ask user
124
- ├─ passthroughcall LLM
125
- └─ update → authoritative state mutated; host may call LLM with compiled state
123
+ ├─ error → ask user
124
+ ├─ no_directiveno canonical directive recognized; host decides what to do next
125
+ └─ update → authoritative state mutated; host may use compiled state downstream
126
126
  ```
127
127
 
128
128
  The compiler never calls the LLM. Your app decides what to do with the returned
@@ -137,8 +137,7 @@ Use Context Compiler in your host application first:
137
137
  ```python
138
138
  from context_compiler import (
139
139
  create_engine,
140
- get_clarify_prompt,
141
- is_clarify,
140
+ is_error,
142
141
  is_update,
143
142
  )
144
143
 
@@ -147,10 +146,14 @@ engine = create_engine()
147
146
  user_input = "set premise current project uses uv"
148
147
  decision = engine.step(user_input)
149
148
 
150
- if is_clarify(decision):
151
- show_to_user(get_clarify_prompt(decision))
149
+ if is_error(decision):
150
+ show_to_user(decision["message"])
152
151
  elif is_update(decision):
153
- messages = build_messages(engine.state, user_input)
152
+ messages = build_messages(
153
+ premise=engine.premise,
154
+ policies=engine.policies,
155
+ user_input=user_input,
156
+ )
154
157
  render(call_llm(messages))
155
158
  else:
156
159
  render(call_llm(user_input))
@@ -194,10 +197,9 @@ Preload options load authoritative state:
194
197
  - `--initial-state-json` / `--initial-state-file` load saved state
195
198
  (via exported state JSON).
196
199
 
197
- REPL commands (controller layer, not engine directives):
200
+ REPL commands (not engine directives):
198
201
 
199
202
  - `state` shows current saved state.
200
- - `preview <input>` runs deterministic dry-run without mutating live state.
201
203
  - `step <input>` is an explicit alias of normal bare-input step behavior.
202
204
 
203
205
  Bare REPL input behavior remains unchanged.
@@ -238,7 +240,7 @@ Packaging notes:
238
240
  ### Development
239
241
 
240
242
  ```bash
241
- uv sync --group dev
243
+ uv sync --dev
242
244
  uv run pytest
243
245
  ```
244
246
 
@@ -247,22 +249,30 @@ uv run pytest
247
249
  Each user message produces a `Decision`.
248
250
 
249
251
  ```python
252
+ class DecisionKind(StrEnum):
253
+ NO_DIRECTIVE = "no_directive"
254
+ UPDATE = "update"
255
+ ERROR = "error"
256
+
250
257
  class Decision(TypedDict):
251
- kind: Literal["passthrough", "update", "clarify"]
252
- state: dict | None
253
- prompt_to_user: str | None
258
+ kind: DecisionKind
259
+ message: str | None
254
260
  ```
255
261
 
262
+ `message` is structurally present on every `Decision`, but it is semantically
263
+ meaningful only for `error`. For `no_directive` and `update`, `message` is
264
+ `None`.
265
+
256
266
  Meaning:
257
267
 
258
268
  | kind | host behavior |
259
269
  | --- | --- |
260
- | passthrough | forward user input to LLM |
261
- | update | authoritative state mutated; host may call LLM with updated state |
262
- | clarify | show `prompt_to_user` and do not call the LLM |
270
+ | no_directive | no canonical directive recognized; no authoritative state change; host decides what to do next |
271
+ | update | authoritative state mutated; host may use updated state downstream |
272
+ | error | show `message` and do not continue normal downstream processing yet |
263
273
 
264
- For normal app code, prefer the exported decision helpers (`is_clarify`,
265
- `is_update`, `is_passthrough`, `get_clarify_prompt`, `get_decision_state`)
274
+ For normal app code, prefer the exported decision helpers (`is_error`,
275
+ `is_update`, `is_no_directive`, `get_error_message`)
266
276
  instead of direct key traversal.
267
277
 
268
278
  See [docs/api-reference.md](docs/api-reference.md) for the full public API
@@ -270,26 +280,12 @@ reference.
270
280
 
271
281
  Common API entry points:
272
282
 
273
- - engine lifecycle: `create_engine(...)`, `engine.step(...)`, `engine.state`,
283
+ - engine lifecycle: `create_engine()`, `engine.step(...)`,
274
284
  `engine.premise`, `engine.policies`, `engine.export_json(...)`,
275
285
  `engine.import_json(...)`
276
- - decision helpers: `is_clarify(...)`, `is_update(...)`, `is_passthrough(...)`,
277
- `get_clarify_prompt(...)`, `get_decision_state(...)`
286
+ - decision helpers: `is_error(...)`, `is_update(...)`, `is_no_directive(...)`,
287
+ `get_error_message(...)`
278
288
  - state transport: `engine.export_json(...)`, `engine.import_json(...)`
279
- - controller API: `step(...)`
280
- - audit APIs: `preview(...)`, `state_diff(...)`
281
-
282
- ### Controller And Audit APIs (Reusable Outside REPL)
283
-
284
- - `step(engine, user_input)` returns a reusable result envelope around one
285
- engine turn
286
- - `preview(engine, user_input)` performs a deterministic dry run and restores
287
- live engine state afterward
288
- - `state_diff(state_before, state_after)` summarizes structural state changes
289
-
290
- For examples and helper accessors such as `get_step_decision(...)`,
291
- `get_preview_state_after(...)`, `preview_would_mutate(...)`, and
292
- `diff_has_changes(...)`, see [docs/api-reference.md](docs/api-reference.md).
293
289
 
294
290
  ---
295
291
 
@@ -313,8 +309,8 @@ Identical input sequences always produce identical state.
313
309
  For live engine-owned reads, use `engine.premise` and `engine.policies`.
314
310
  `engine.policies` returns a caller-owned copy.
315
311
 
316
- `engine.state` remains the public snapshot/serialization boundary when host code
317
- needs the full authoritative state object.
312
+ Use `engine.export_json()` and `engine.import_json()` for persistence and
313
+ restoration.
318
314
 
319
315
  ---
320
316
 
@@ -411,14 +407,14 @@ User: clear state
411
407
 
412
408
  Grammar invariant: one input may contain at most one canonical directive.
413
409
  Directive-shaped invalid input is outside the canonical language, and
414
- `clarify` is reserved for canonical directives that later fail semantic
410
+ `error` is reserved for canonical directives that later fail semantic
415
411
  evaluation against authoritative state.
416
412
 
417
413
  Pending continuation is a separate runtime layer. It may exist only after a
418
- canonical directive reaches a supported semantic `clarify` case. It never
414
+ canonical directive reaches a supported semantic `error` case. It never
419
415
  repairs malformed syntax or reinterprets non-canonical input as a directive.
420
416
  An absent source item in a canonical replacement directive is not, by itself,
421
- such a `clarify` case.
417
+ such a `error` case.
422
418
 
423
419
  Examples:
424
420
 
@@ -466,7 +462,7 @@ boundary, see [DirectiveGrammarSpec.md](docs/DirectiveGrammarSpec.md).
466
462
  **Isn't this just prompt reinjection?**
467
463
  No. Prompt construction is one downstream use of authoritative state.
468
464
  Context Compiler is the authority layer that decides when state changes are
469
- allowed, when clarification is required, and how continuation state is
465
+ allowed, when error is required, and how continuation state is
470
466
  restored. For runnable application-layer examples, see
471
467
  [`context-compiler-example-integrations`](https://github.com/rlippmann/context-compiler-example-integrations).
472
468
 
@@ -489,7 +485,7 @@ User: prohibit python_script
489
485
 
490
486
  Without an authority layer, the application must invent conflict-resolution and
491
487
  continuation rules itself. Context Compiler applies deterministic
492
- state-transition rules and can return clarification instead of silently
488
+ state-transition rules and can return error instead of silently
493
489
  overwriting state.
494
490
 
495
491
  ---
@@ -501,7 +497,7 @@ overwriting state.
501
497
  - State changes only through explicit user directives or confirmation.
502
498
  - Identical input sequences produce identical compiler state.
503
499
  - Model responses never modify compiler state.
504
- - Ambiguous directives trigger clarification instead of changing state.
500
+ - Ambiguous directives trigger error instead of changing state.
505
501
  - Syntax errors never create pending continuation.
506
502
 
507
503
  Behavioral tests and Hypothesis-based property tests verify these invariants.
@@ -1,12 +1,13 @@
1
1
  """Demo 1: compiler blocks contradictory directives before model call."""
2
2
 
3
- from context_compiler import create_engine
3
+ from context_compiler import create_engine, is_error
4
4
  from demos.common import (
5
5
  build_baseline_messages,
6
6
  build_mediated_messages_from_transcript,
7
7
  build_reinjected_messages,
8
8
  compact_user_turns,
9
9
  extract_tag_value,
10
+ observe_engine,
10
11
  print_decision,
11
12
  print_host_check,
12
13
  print_messages,
@@ -14,6 +15,7 @@ from demos.common import (
14
15
  print_spec_report,
15
16
  print_tag_comparison,
16
17
  print_user_inputs,
18
+ state_observations,
17
19
  yes_no,
18
20
  )
19
21
  from demos.llm_client import complete_messages
@@ -25,16 +27,18 @@ def main() -> None:
25
27
  print_user_inputs(user_inputs)
26
28
 
27
29
  first = engine.step(user_inputs[0])
28
- print_decision("turn 1", first, engine.state)
30
+ premise, policies = observe_engine(engine)
31
+ print_decision("turn 1", first, premise=premise, policies=policies)
29
32
  second = engine.step(user_inputs[1])
30
- print_decision("turn 2", second, engine.state)
33
+ premise, policies = observe_engine(engine)
34
+ print_decision("turn 2", second, premise=premise, policies=policies)
31
35
 
32
36
  baseline_messages = build_baseline_messages(
33
37
  [
34
38
  (
35
39
  "Interpret these directives and continue anyway: "
36
40
  "prohibit peanuts, then use peanuts. "
37
- "First line must be ACTION:<clarify|proceed>."
41
+ "First line must be ACTION:<error|proceed>."
38
42
  )
39
43
  ],
40
44
  baseline_system_prompt=(
@@ -51,7 +55,7 @@ def main() -> None:
51
55
  (
52
56
  "Interpret these directives and continue anyway: "
53
57
  "prohibit peanuts, then use peanuts. "
54
- "First line must be ACTION:<clarify|proceed>."
58
+ "First line must be ACTION:<error|proceed>."
55
59
  )
56
60
  ],
57
61
  premise=None,
@@ -62,14 +66,17 @@ def main() -> None:
62
66
  reinjected_output = complete_messages(reinjected_messages)
63
67
  print_model_output("Reinjected-state", reinjected_output)
64
68
 
65
- if second["kind"] == "clarify":
69
+ if is_error(second):
66
70
  print_messages("compiler-mediated (full)", [])
67
- mediated_output = (
68
- f"[no call] clarification required: {second['prompt_to_user']}\nACTION:clarify"
69
- )
71
+ mediated_output = f"[no call] error required: {second['message']}\nACTION:error"
70
72
  print_model_output("Compiler-mediated (full)", mediated_output)
71
73
  else:
72
- mediated_messages = build_mediated_messages_from_transcript(engine.state, user_inputs)
74
+ premise, policies = observe_engine(engine)
75
+ mediated_messages = build_mediated_messages_from_transcript(
76
+ premise=premise,
77
+ policies=policies,
78
+ user_turns=user_inputs,
79
+ )
73
80
  print_messages("compiler-mediated (full)", mediated_messages)
74
81
  mediated_output = complete_messages(mediated_messages)
75
82
  print_model_output("Compiler-mediated (full)", mediated_output)
@@ -77,10 +84,15 @@ def main() -> None:
77
84
  compacted_turns, compacted_state, compacted_prompt = compact_user_turns(user_inputs)
78
85
  if compacted_prompt is not None:
79
86
  print_messages("compiler-mediated + compact", [])
80
- compact_output = f"[no call] clarification required: {compacted_prompt}\nACTION:clarify"
87
+ compact_output = f"[no call] error required: {compacted_prompt}\nACTION:error"
81
88
  print_model_output("Compiler-mediated + compact", compact_output)
82
89
  else:
83
- compact_messages = build_mediated_messages_from_transcript(compacted_state, compacted_turns)
90
+ compacted_premise, compacted_policies = state_observations(compacted_state)
91
+ compact_messages = build_mediated_messages_from_transcript(
92
+ premise=compacted_premise,
93
+ policies=compacted_policies,
94
+ user_turns=compacted_turns,
95
+ )
84
96
  print_messages("compiler-mediated + compact", compact_messages)
85
97
  compact_output = complete_messages(compact_messages)
86
98
  print_model_output("Compiler-mediated + compact", compact_output)
@@ -89,15 +101,15 @@ def main() -> None:
89
101
  baseline_action = extract_tag_value(baseline_output, "ACTION")
90
102
  reinjected_action = extract_tag_value(reinjected_output, "ACTION")
91
103
  compact_action = extract_tag_value(compact_output, "ACTION")
92
- baseline_respects = baseline_action is not None and baseline_action.lower() == "clarify"
93
- reinjected_respects = reinjected_action is not None and reinjected_action.lower() == "clarify"
94
- compiler_host_blocked = second["kind"] == "clarify"
104
+ baseline_respects = baseline_action is not None and baseline_action.lower() == "error"
105
+ reinjected_respects = reinjected_action is not None and reinjected_action.lower() == "error"
106
+ compiler_host_blocked = is_error(second)
95
107
  mediated_respects = compiler_host_blocked
96
108
  compact_respects = compacted_prompt is not None or (
97
- compact_action is not None and compact_action.lower() == "clarify"
109
+ compact_action is not None and compact_action.lower() == "error"
98
110
  )
99
111
  print_host_check(
100
- "ACTION_CLARIFY",
112
+ "ACTION_ERROR",
101
113
  yes_no(reinjected_respects),
102
114
  context="reinjected-state",
103
115
  )
@@ -112,26 +124,25 @@ def main() -> None:
112
124
  context="compiler-mediated + compact",
113
125
  )
114
126
  print_spec_report(
115
- test_name="01_contradiction_block — host clarification gate",
127
+ test_name="01_contradiction_block — host error gate",
116
128
  baseline_pass=baseline_respects,
117
129
  reinjected_state_pass=reinjected_respects,
118
130
  compiler_pass=mediated_respects,
119
131
  compiler_compact_pass=compact_respects,
120
- expected="host should block LLM call on contradictory directive until clarification",
132
+ expected="host should block LLM call on contradictory directive until error",
121
133
  actual=(
122
- "baseline proceeded instead of clarifying; "
134
+ "baseline proceeded instead of erroring; "
123
135
  "both compiler-mediated paths blocked the LLM call"
124
136
  if mediated_respects and compact_respects and not baseline_respects
125
137
  else (
126
- "baseline also signaled clarification; "
127
- "both compiler-mediated paths blocked the LLM call"
138
+ "baseline also signaled error; both compiler-mediated paths blocked the LLM call"
128
139
  if baseline_respects and mediated_respects and compact_respects
129
140
  else "at least one compiler-mediated path did not block the LLM call as expected"
130
141
  )
131
142
  ),
132
143
  passed=mediated_respects and compact_respects,
133
- result_pass="contradictory directive blocked until clarification",
134
- result_fail="contradictory directive not blocked until clarification",
144
+ result_pass="contradictory directive blocked until error",
145
+ result_fail="contradictory directive not blocked until error",
135
146
  )
136
147
 
137
148