context-compiler 0.9.0.dev12__tar.gz → 0.9.0.dev14__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.
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev14}/PKG-INFO +118 -48
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev14}/README.md +117 -47
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev14}/demos/01_llm_contradiction_error.py +4 -4
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev14}/demos/06_llm_context_compaction.py +2 -2
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev14}/demos/08_llm_replacement_precondition.py +4 -4
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev14}/demos/README.md +6 -7
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev14}/demos/common.py +8 -10
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev14}/demos/run_demo.py +1 -2
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev14}/examples/03_ambiguity_with_error.py +3 -3
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev14}/examples/05_llm_integration_pattern.py +7 -7
- context_compiler-0.9.0.dev14/examples/08_apply_directive_decisions.py +95 -0
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev14}/examples/README.md +10 -0
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev14}/examples/_util.py +7 -5
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev14}/pyproject.toml +1 -1
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev14}/src/context_compiler/__init__.py +10 -13
- context_compiler-0.9.0.dev14/src/context_compiler/decision.py +107 -0
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev14}/src/context_compiler/engine.py +101 -45
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev14}/src/context_compiler/grammar.py +31 -3
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev14}/src/context_compiler/repl.py +49 -14
- context_compiler-0.9.0.dev12/demos/09_llm_confirmation_no_directive.py +0 -173
- context_compiler-0.9.0.dev12/src/context_compiler/decision_helpers.py +0 -30
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev14}/.gitignore +0 -0
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev14}/LICENSE +0 -0
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev14}/demos/02_llm_constraint_guardrail.py +0 -0
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev14}/demos/03_llm_premise_guardrail.py +0 -0
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev14}/demos/04_llm_tool_denylist_guardrail.py +0 -0
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev14}/demos/05_llm_prompt_drift_vs_state.py +0 -0
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev14}/demos/07_llm_prompt_vs_state.py +0 -0
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev14}/demos/__init__.py +0 -0
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev14}/demos/llm_client.py +0 -0
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev14}/examples/01_persistent_guardrails.py +0 -0
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev14}/examples/02_configuration_and_correction.py +0 -0
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev14}/examples/04_tool_governance_denylist.py +0 -0
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev14}/examples/06_step_sequence_and_state_restore.py +0 -0
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev14}/examples/07_single_policy_correction.py +0 -0
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev14}/host_support/__init__.py +0 -0
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev14}/host_support/provider_mode.py +0 -0
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev14}/src/context_compiler/const.py +0 -0
- {context_compiler-0.9.0.dev12 → context_compiler-0.9.0.dev14}/src/context_compiler/py.typed +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: context-compiler
|
|
3
|
-
Version: 0.9.0.
|
|
3
|
+
Version: 0.9.0.dev14
|
|
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
|
|
@@ -25,11 +25,11 @@ Description-Content-Type: text/markdown
|
|
|
25
25
|
[](https://pypi.org/project/context-compiler/)
|
|
26
26
|
[](https://pypi.org/project/context-compiler/)
|
|
27
27
|
[](https://pypi.org/project/context-compiler/)
|
|
28
|
+
[](https://codecov.io/gh/rlippmann/context-compiler)
|
|
28
29
|
|
|
29
30
|
Context Compiler is a deterministic conversational state authority for LLM applications.
|
|
30
|
-
It handles canonical directive execution, semantic validation,
|
|
31
|
-
|
|
32
|
-
the host.
|
|
31
|
+
It handles canonical directive execution, semantic validation, terminal error
|
|
32
|
+
decisions, advisory repairs, and structured authoritative state for the host.
|
|
33
33
|
|
|
34
34
|
## What Context Compiler provides
|
|
35
35
|
|
|
@@ -37,7 +37,7 @@ Context Compiler gives hosts fixed state rules:
|
|
|
37
37
|
|
|
38
38
|
- handle canonical explicit state changes with deterministic rules
|
|
39
39
|
- error instead of silent overwrite for blocked/ambiguous changes
|
|
40
|
-
-
|
|
40
|
+
- return structured terminal errors with explicitly selectable advisory repairs
|
|
41
41
|
- export and import authoritative state for host-managed persistence
|
|
42
42
|
- produce structured authoritative state for downstream host decisions
|
|
43
43
|
|
|
@@ -84,8 +84,8 @@ The architecture has three layers:
|
|
|
84
84
|
directive-shaped syntax, or ordinary no_directive
|
|
85
85
|
- semantic evaluation decides whether a canonical directive updates state,
|
|
86
86
|
clarifies, or no-ops
|
|
87
|
-
- semantic
|
|
88
|
-
|
|
87
|
+
- semantic evaluation returns an update or a terminal error with structured
|
|
88
|
+
advisory repairs when a canonical directive conflicts with state
|
|
89
89
|
|
|
90
90
|
### Explicit directive
|
|
91
91
|
|
|
@@ -96,14 +96,14 @@ set premise concise replies
|
|
|
96
96
|
- Base model: silently accepts / rewrites
|
|
97
97
|
- Context Compiler: applies a repeatable state update
|
|
98
98
|
|
|
99
|
-
###
|
|
99
|
+
### Compound directive rejection
|
|
100
100
|
|
|
101
101
|
```text
|
|
102
102
|
use docker and prohibit peanuts
|
|
103
103
|
```
|
|
104
104
|
|
|
105
105
|
- Without an authority layer: host/model behavior varies
|
|
106
|
-
- Context Compiler: treats this as invalid directive-shaped syntax
|
|
106
|
+
- Context Compiler: treats this as invalid directive-shaped syntax and keeps authoritative state unchanged
|
|
107
107
|
|
|
108
108
|
### State-dependent operation
|
|
109
109
|
|
|
@@ -114,7 +114,7 @@ use podman instead of docker
|
|
|
114
114
|
|
|
115
115
|
- Without explicit state transition rules: behavior depends on host/model handling
|
|
116
116
|
- Context Compiler: applies the deterministic resulting transition when
|
|
117
|
-
`docker` is
|
|
117
|
+
`docker` is present and `use podman` is otherwise valid; other semantic
|
|
118
118
|
conflicts may still error
|
|
119
119
|
|
|
120
120
|
### Lifecycle enforcement
|
|
@@ -159,8 +159,9 @@ Use Context Compiler in your host application first:
|
|
|
159
159
|
```python
|
|
160
160
|
from context_compiler import (
|
|
161
161
|
Engine,
|
|
162
|
-
|
|
163
|
-
|
|
162
|
+
NoDirectiveDecision,
|
|
163
|
+
SemanticErrorDecision,
|
|
164
|
+
UpdateDecision,
|
|
164
165
|
)
|
|
165
166
|
|
|
166
167
|
engine = Engine()
|
|
@@ -168,16 +169,16 @@ engine = Engine()
|
|
|
168
169
|
user_input = "set premise current project uses uv"
|
|
169
170
|
decision = engine.step(user_input)
|
|
170
171
|
|
|
171
|
-
if
|
|
172
|
-
show_to_user(decision
|
|
173
|
-
elif
|
|
172
|
+
if isinstance(decision, SemanticErrorDecision):
|
|
173
|
+
show_to_user(decision.message)
|
|
174
|
+
elif isinstance(decision, UpdateDecision):
|
|
174
175
|
messages = build_messages(
|
|
175
176
|
premise=engine.premise,
|
|
176
177
|
policies=engine.policies,
|
|
177
178
|
user_input=user_input,
|
|
178
179
|
)
|
|
179
180
|
render(call_llm(messages))
|
|
180
|
-
|
|
181
|
+
elif isinstance(decision, NoDirectiveDecision):
|
|
181
182
|
render(call_llm(user_input))
|
|
182
183
|
```
|
|
183
184
|
|
|
@@ -188,7 +189,7 @@ For runnable application-layer examples, see
|
|
|
188
189
|
[`context-compiler-example-integrations`](https://github.com/rlippmann/context-compiler-example-integrations).
|
|
189
190
|
That companion repository shows enforcement points built on compiler state,
|
|
190
191
|
including retrieval filtering, schema selection, tool gating, execution
|
|
191
|
-
authorization, gateway middleware, runtime
|
|
192
|
+
authorization, gateway middleware, runtime state handling, and prompt
|
|
192
193
|
construction.
|
|
193
194
|
|
|
194
195
|
## Does it Work?
|
|
@@ -235,6 +236,12 @@ for non-interactive usage.
|
|
|
235
236
|
context-compiler --json < input.txt
|
|
236
237
|
```
|
|
237
238
|
|
|
239
|
+
The JSON output uses `output_version: 2`. Decision payloads expose structured
|
|
240
|
+
fields: updates include `changed`; semantic errors include `failure`, the
|
|
241
|
+
failed canonical `directive`, ordered advisory `repairs`, and `message`.
|
|
242
|
+
These fields are a CLI projection of ephemeral Decisions, not Decision object
|
|
243
|
+
serialization. Repairs are never applied automatically.
|
|
244
|
+
|
|
238
245
|
Preload options load authoritative state:
|
|
239
246
|
|
|
240
247
|
- `--initial-state-json` / `--initial-state-file` load saved state
|
|
@@ -266,9 +273,15 @@ uv sync --dev
|
|
|
266
273
|
uv run pytest
|
|
267
274
|
```
|
|
268
275
|
|
|
276
|
+
CI enforces 100% coverage for the core `src/context_compiler` package. The
|
|
277
|
+
coverage badge represents that authoritative core-package target, not the
|
|
278
|
+
entire repository.
|
|
279
|
+
|
|
269
280
|
## Decision API
|
|
270
281
|
|
|
271
|
-
Each user message produces
|
|
282
|
+
Each user message produces one immutable `Decision` variant. Use concrete
|
|
283
|
+
variants with `isinstance` or pattern matching; use `kind` when generic code
|
|
284
|
+
needs the stable discriminator.
|
|
272
285
|
|
|
273
286
|
```python
|
|
274
287
|
class DecisionKind(StrEnum):
|
|
@@ -276,26 +289,72 @@ class DecisionKind(StrEnum):
|
|
|
276
289
|
UPDATE = "update"
|
|
277
290
|
ERROR = "error"
|
|
278
291
|
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
292
|
+
Decision = NoDirectiveDecision | UpdateDecision | SemanticErrorDecision
|
|
293
|
+
|
|
294
|
+
@dataclass(frozen=True, slots=True)
|
|
295
|
+
class NoDirectiveDecision:
|
|
296
|
+
kind = DecisionKind.NO_DIRECTIVE
|
|
297
|
+
|
|
298
|
+
@dataclass(frozen=True, slots=True)
|
|
299
|
+
class UpdateDecision:
|
|
300
|
+
kind = DecisionKind.UPDATE
|
|
301
|
+
changed: bool
|
|
302
|
+
|
|
303
|
+
@dataclass(frozen=True, slots=True)
|
|
304
|
+
class SemanticErrorDecision:
|
|
305
|
+
kind = DecisionKind.ERROR
|
|
306
|
+
failure: SemanticFailure
|
|
307
|
+
directive: CanonicalDirective
|
|
308
|
+
repairs: tuple[CanonicalDirective, ...]
|
|
309
|
+
message: str
|
|
282
310
|
```
|
|
283
311
|
|
|
284
|
-
`
|
|
285
|
-
|
|
286
|
-
`
|
|
312
|
+
`UpdateDecision.changed` reports whether authoritative state actually changed.
|
|
313
|
+
An accepted idempotent directive is still an `update` with
|
|
314
|
+
`changed=False`.
|
|
315
|
+
|
|
316
|
+
`SemanticErrorDecision.message` is derived human-readable text. Callers should
|
|
317
|
+
use `failure` for machine decisions rather than parsing the message.
|
|
318
|
+
|
|
319
|
+
`directive` is the canonical directive that failed semantic evaluation.
|
|
320
|
+
`repairs` is an ordered tuple of advisory canonical directives. Repairs are
|
|
321
|
+
never applied automatically; a host must explicitly submit a selected repair
|
|
322
|
+
through `engine.apply_directive(...)`.
|
|
323
|
+
|
|
324
|
+
The normative repair mapping is:
|
|
325
|
+
|
|
326
|
+
| Failure | Ordered advisory repairs |
|
|
327
|
+
| --- | --- |
|
|
328
|
+
| `PREMISE_ALREADY_SET` | `change premise to <requested value>` |
|
|
329
|
+
| `PREMISE_NOT_SET` | `set premise <requested value>` |
|
|
330
|
+
| `ITEM_PROHIBITED` | `remove policy <item>`; `use <item>` |
|
|
331
|
+
| `ITEM_ALREADY_IN_USE` | `remove policy <item>`; `prohibit <item>` |
|
|
332
|
+
| `REPLACEMENT_TARGET_PROHIBITED` | `remove policy <target>`; retry the original replacement directive |
|
|
333
|
+
| `REPLACEMENT_SOURCE_PROHIBITED` | no repair (`()`) |
|
|
334
|
+
| `REPLACEMENT_SOURCE_MISSING` | no repair (`()`) |
|
|
335
|
+
|
|
336
|
+
Repairs are canonical, ordered, and advisory only. The engine never applies
|
|
337
|
+
them automatically; hosts explicitly select and submit any repair they want to
|
|
338
|
+
use. `message` is presentation data, so control flow must use `failure` and
|
|
339
|
+
the structured directives rather than parse message text.
|
|
287
340
|
|
|
288
341
|
Meaning:
|
|
289
342
|
|
|
290
343
|
| kind | host behavior |
|
|
291
344
|
| --- | --- |
|
|
292
345
|
| no_directive | no canonical directive recognized; no authoritative state change; host decides what to do next |
|
|
293
|
-
| update |
|
|
294
|
-
| error | show `message
|
|
346
|
+
| update | canonical directive was accepted; inspect `changed` and use updated state downstream |
|
|
347
|
+
| error | canonical directive was rejected semantically; inspect `failure`, show `message`, and optionally offer `repairs` |
|
|
348
|
+
|
|
349
|
+
`engine.step(...)` is the raw input boundary: it parses user input, may return
|
|
350
|
+
`NoDirectiveDecision` when no canonical directive is produced, and performs
|
|
351
|
+
semantic evaluation only after canonical parsing succeeds. The canonical
|
|
352
|
+
execution boundary is `engine.apply_directive(...)`, which accepts only a
|
|
353
|
+
`CanonicalDirective` and returns only `UpdateDecision` or
|
|
354
|
+
`SemanticErrorDecision`.
|
|
295
355
|
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
instead of direct key traversal.
|
|
356
|
+
Grammar failures do not produce semantic errors. A semantic error is possible
|
|
357
|
+
only after a canonical directive has parsed successfully.
|
|
299
358
|
|
|
300
359
|
See [docs/api-reference.md](docs/api-reference.md) for the full public API
|
|
301
360
|
reference.
|
|
@@ -305,8 +364,8 @@ Common API entry points:
|
|
|
305
364
|
- engine lifecycle: `Engine()`, `engine.step(...)`,
|
|
306
365
|
`engine.premise`, `engine.policies`, `engine.export_json(...)`,
|
|
307
366
|
`engine.import_json(...)`
|
|
308
|
-
- decision
|
|
309
|
-
`
|
|
367
|
+
- decision variants: `NoDirectiveDecision`, `UpdateDecision`,
|
|
368
|
+
`SemanticErrorDecision`, `SemanticFailure`
|
|
310
369
|
- state transport: `engine.export_json(...)`, `engine.import_json(...)`
|
|
311
370
|
|
|
312
371
|
---
|
|
@@ -387,8 +446,8 @@ the compiler.
|
|
|
387
446
|
|
|
388
447
|
- They transport **authoritative state only**
|
|
389
448
|
- Hosts own any broader interaction or session workflow around that state
|
|
390
|
-
-
|
|
391
|
-
|
|
449
|
+
- Decision objects and advisory repairs are not persisted; persistence carries
|
|
450
|
+
authoritative state only
|
|
392
451
|
|
|
393
452
|
---
|
|
394
453
|
|
|
@@ -426,16 +485,20 @@ User: reset policies
|
|
|
426
485
|
User: clear state
|
|
427
486
|
```
|
|
428
487
|
|
|
429
|
-
Grammar invariant:
|
|
488
|
+
Grammar invariant: a single input never applies more than one canonical
|
|
489
|
+
directive.
|
|
430
490
|
Directive-shaped invalid input is outside the canonical language, and
|
|
431
491
|
`error` is reserved for canonical directives that later fail semantic
|
|
432
492
|
evaluation against authoritative state.
|
|
433
493
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
494
|
+
A semantic error is a terminal result for the current input. It leaves
|
|
495
|
+
authoritative state unchanged and returns the failure classification, failed
|
|
496
|
+
canonical directive, ordered advisory repairs, and presentation message.
|
|
497
|
+
Repairs are canonical directives. They are advisory only, are never applied
|
|
498
|
+
automatically, and require explicit host selection and submission through
|
|
499
|
+
`engine.apply_directive(...)`. An absent source item in a canonical replacement
|
|
500
|
+
directive is a semantic `error` and does not authorize degradation to plain
|
|
501
|
+
`use`.
|
|
439
502
|
|
|
440
503
|
Examples:
|
|
441
504
|
|
|
@@ -447,18 +510,25 @@ clear state
|
|
|
447
510
|
|
|
448
511
|
Invalid:
|
|
449
512
|
use docker and prohibit peanuts
|
|
450
|
-
set premise vegetarian and use docker
|
|
451
513
|
clear state then set premise new project
|
|
514
|
+
|
|
515
|
+
Premise payload (opaque):
|
|
516
|
+
set premise vegetarian and use docker
|
|
452
517
|
```
|
|
453
518
|
|
|
519
|
+
Policy compounds such as `use docker and prohibit peanuts` remain invalid;
|
|
520
|
+
premise `VALUE` is opaque and may contain directive-like words.
|
|
521
|
+
|
|
454
522
|
Quote behavior follows the current grammar literally:
|
|
455
523
|
|
|
456
524
|
```text
|
|
457
|
-
Passthrough:
|
|
525
|
+
Passthrough (`no_directive`):
|
|
458
526
|
"use docker and prohibit peanuts"
|
|
459
527
|
|
|
460
|
-
Invalid:
|
|
528
|
+
Invalid directive:
|
|
461
529
|
use "docker and prohibit peanuts"
|
|
530
|
+
|
|
531
|
+
Canonical directive (`set premise`):
|
|
462
532
|
set premise "use docker and prohibit peanuts"
|
|
463
533
|
```
|
|
464
534
|
|
|
@@ -483,8 +553,8 @@ boundary, see [DirectiveGrammarSpec.md](docs/DirectiveGrammarSpec.md).
|
|
|
483
553
|
**Isn't this just prompt reinjection?**
|
|
484
554
|
No. Prompt construction is one downstream use of authoritative state.
|
|
485
555
|
Context Compiler is the authority layer that decides when state changes are
|
|
486
|
-
allowed, when error is required, and
|
|
487
|
-
|
|
556
|
+
allowed, when a terminal error is required, and which advisory repairs are
|
|
557
|
+
available. For runnable application-layer examples, see
|
|
488
558
|
[`context-compiler-example-integrations`](https://github.com/rlippmann/context-compiler-example-integrations).
|
|
489
559
|
|
|
490
560
|
Human-facing interpretation is a separate concern. If you want to recognize
|
|
@@ -505,8 +575,8 @@ User: prohibit python_script
|
|
|
505
575
|
```
|
|
506
576
|
|
|
507
577
|
Without an authority layer, the application must invent conflict-resolution and
|
|
508
|
-
|
|
509
|
-
|
|
578
|
+
repair rules itself. Context Compiler applies deterministic state-transition
|
|
579
|
+
rules and can return a terminal error instead of silently
|
|
510
580
|
overwriting state.
|
|
511
581
|
|
|
512
582
|
---
|
|
@@ -515,11 +585,11 @@ overwriting state.
|
|
|
515
585
|
|
|
516
586
|
### Guarantees
|
|
517
587
|
|
|
518
|
-
- State changes only through
|
|
588
|
+
- State changes only through canonical directives that pass semantic evaluation.
|
|
519
589
|
- Identical input sequences produce identical compiler state.
|
|
520
590
|
- Model responses never modify compiler state.
|
|
521
591
|
- Ambiguous directives trigger error instead of changing state.
|
|
522
|
-
- Syntax errors never
|
|
592
|
+
- Syntax errors never produce semantic errors or state changes.
|
|
523
593
|
|
|
524
594
|
Behavioral tests and Hypothesis-based property tests verify these invariants.
|
|
525
595
|
|
|
@@ -3,11 +3,11 @@
|
|
|
3
3
|
[](https://pypi.org/project/context-compiler/)
|
|
4
4
|
[](https://pypi.org/project/context-compiler/)
|
|
5
5
|
[](https://pypi.org/project/context-compiler/)
|
|
6
|
+
[](https://codecov.io/gh/rlippmann/context-compiler)
|
|
6
7
|
|
|
7
8
|
Context Compiler is a deterministic conversational state authority for LLM applications.
|
|
8
|
-
It handles canonical directive execution, semantic validation,
|
|
9
|
-
|
|
10
|
-
the host.
|
|
9
|
+
It handles canonical directive execution, semantic validation, terminal error
|
|
10
|
+
decisions, advisory repairs, and structured authoritative state for the host.
|
|
11
11
|
|
|
12
12
|
## What Context Compiler provides
|
|
13
13
|
|
|
@@ -15,7 +15,7 @@ Context Compiler gives hosts fixed state rules:
|
|
|
15
15
|
|
|
16
16
|
- handle canonical explicit state changes with deterministic rules
|
|
17
17
|
- error instead of silent overwrite for blocked/ambiguous changes
|
|
18
|
-
-
|
|
18
|
+
- return structured terminal errors with explicitly selectable advisory repairs
|
|
19
19
|
- export and import authoritative state for host-managed persistence
|
|
20
20
|
- produce structured authoritative state for downstream host decisions
|
|
21
21
|
|
|
@@ -62,8 +62,8 @@ The architecture has three layers:
|
|
|
62
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
|
-
- semantic
|
|
66
|
-
|
|
65
|
+
- semantic evaluation returns an update or a terminal error with structured
|
|
66
|
+
advisory repairs when a canonical directive conflicts with state
|
|
67
67
|
|
|
68
68
|
### Explicit directive
|
|
69
69
|
|
|
@@ -74,14 +74,14 @@ set premise concise replies
|
|
|
74
74
|
- Base model: silently accepts / rewrites
|
|
75
75
|
- Context Compiler: applies a repeatable state update
|
|
76
76
|
|
|
77
|
-
###
|
|
77
|
+
### Compound directive rejection
|
|
78
78
|
|
|
79
79
|
```text
|
|
80
80
|
use docker and prohibit peanuts
|
|
81
81
|
```
|
|
82
82
|
|
|
83
83
|
- Without an authority layer: host/model behavior varies
|
|
84
|
-
- Context Compiler: treats this as invalid directive-shaped syntax
|
|
84
|
+
- Context Compiler: treats this as invalid directive-shaped syntax and keeps authoritative state unchanged
|
|
85
85
|
|
|
86
86
|
### State-dependent operation
|
|
87
87
|
|
|
@@ -92,7 +92,7 @@ use podman instead of docker
|
|
|
92
92
|
|
|
93
93
|
- Without explicit state transition rules: behavior depends on host/model handling
|
|
94
94
|
- Context Compiler: applies the deterministic resulting transition when
|
|
95
|
-
`docker` is
|
|
95
|
+
`docker` is present and `use podman` is otherwise valid; other semantic
|
|
96
96
|
conflicts may still error
|
|
97
97
|
|
|
98
98
|
### Lifecycle enforcement
|
|
@@ -137,8 +137,9 @@ Use Context Compiler in your host application first:
|
|
|
137
137
|
```python
|
|
138
138
|
from context_compiler import (
|
|
139
139
|
Engine,
|
|
140
|
-
|
|
141
|
-
|
|
140
|
+
NoDirectiveDecision,
|
|
141
|
+
SemanticErrorDecision,
|
|
142
|
+
UpdateDecision,
|
|
142
143
|
)
|
|
143
144
|
|
|
144
145
|
engine = Engine()
|
|
@@ -146,16 +147,16 @@ engine = Engine()
|
|
|
146
147
|
user_input = "set premise current project uses uv"
|
|
147
148
|
decision = engine.step(user_input)
|
|
148
149
|
|
|
149
|
-
if
|
|
150
|
-
show_to_user(decision
|
|
151
|
-
elif
|
|
150
|
+
if isinstance(decision, SemanticErrorDecision):
|
|
151
|
+
show_to_user(decision.message)
|
|
152
|
+
elif isinstance(decision, UpdateDecision):
|
|
152
153
|
messages = build_messages(
|
|
153
154
|
premise=engine.premise,
|
|
154
155
|
policies=engine.policies,
|
|
155
156
|
user_input=user_input,
|
|
156
157
|
)
|
|
157
158
|
render(call_llm(messages))
|
|
158
|
-
|
|
159
|
+
elif isinstance(decision, NoDirectiveDecision):
|
|
159
160
|
render(call_llm(user_input))
|
|
160
161
|
```
|
|
161
162
|
|
|
@@ -166,7 +167,7 @@ For runnable application-layer examples, see
|
|
|
166
167
|
[`context-compiler-example-integrations`](https://github.com/rlippmann/context-compiler-example-integrations).
|
|
167
168
|
That companion repository shows enforcement points built on compiler state,
|
|
168
169
|
including retrieval filtering, schema selection, tool gating, execution
|
|
169
|
-
authorization, gateway middleware, runtime
|
|
170
|
+
authorization, gateway middleware, runtime state handling, and prompt
|
|
170
171
|
construction.
|
|
171
172
|
|
|
172
173
|
## Does it Work?
|
|
@@ -213,6 +214,12 @@ for non-interactive usage.
|
|
|
213
214
|
context-compiler --json < input.txt
|
|
214
215
|
```
|
|
215
216
|
|
|
217
|
+
The JSON output uses `output_version: 2`. Decision payloads expose structured
|
|
218
|
+
fields: updates include `changed`; semantic errors include `failure`, the
|
|
219
|
+
failed canonical `directive`, ordered advisory `repairs`, and `message`.
|
|
220
|
+
These fields are a CLI projection of ephemeral Decisions, not Decision object
|
|
221
|
+
serialization. Repairs are never applied automatically.
|
|
222
|
+
|
|
216
223
|
Preload options load authoritative state:
|
|
217
224
|
|
|
218
225
|
- `--initial-state-json` / `--initial-state-file` load saved state
|
|
@@ -244,9 +251,15 @@ uv sync --dev
|
|
|
244
251
|
uv run pytest
|
|
245
252
|
```
|
|
246
253
|
|
|
254
|
+
CI enforces 100% coverage for the core `src/context_compiler` package. The
|
|
255
|
+
coverage badge represents that authoritative core-package target, not the
|
|
256
|
+
entire repository.
|
|
257
|
+
|
|
247
258
|
## Decision API
|
|
248
259
|
|
|
249
|
-
Each user message produces
|
|
260
|
+
Each user message produces one immutable `Decision` variant. Use concrete
|
|
261
|
+
variants with `isinstance` or pattern matching; use `kind` when generic code
|
|
262
|
+
needs the stable discriminator.
|
|
250
263
|
|
|
251
264
|
```python
|
|
252
265
|
class DecisionKind(StrEnum):
|
|
@@ -254,26 +267,72 @@ class DecisionKind(StrEnum):
|
|
|
254
267
|
UPDATE = "update"
|
|
255
268
|
ERROR = "error"
|
|
256
269
|
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
270
|
+
Decision = NoDirectiveDecision | UpdateDecision | SemanticErrorDecision
|
|
271
|
+
|
|
272
|
+
@dataclass(frozen=True, slots=True)
|
|
273
|
+
class NoDirectiveDecision:
|
|
274
|
+
kind = DecisionKind.NO_DIRECTIVE
|
|
275
|
+
|
|
276
|
+
@dataclass(frozen=True, slots=True)
|
|
277
|
+
class UpdateDecision:
|
|
278
|
+
kind = DecisionKind.UPDATE
|
|
279
|
+
changed: bool
|
|
280
|
+
|
|
281
|
+
@dataclass(frozen=True, slots=True)
|
|
282
|
+
class SemanticErrorDecision:
|
|
283
|
+
kind = DecisionKind.ERROR
|
|
284
|
+
failure: SemanticFailure
|
|
285
|
+
directive: CanonicalDirective
|
|
286
|
+
repairs: tuple[CanonicalDirective, ...]
|
|
287
|
+
message: str
|
|
260
288
|
```
|
|
261
289
|
|
|
262
|
-
`
|
|
263
|
-
|
|
264
|
-
`
|
|
290
|
+
`UpdateDecision.changed` reports whether authoritative state actually changed.
|
|
291
|
+
An accepted idempotent directive is still an `update` with
|
|
292
|
+
`changed=False`.
|
|
293
|
+
|
|
294
|
+
`SemanticErrorDecision.message` is derived human-readable text. Callers should
|
|
295
|
+
use `failure` for machine decisions rather than parsing the message.
|
|
296
|
+
|
|
297
|
+
`directive` is the canonical directive that failed semantic evaluation.
|
|
298
|
+
`repairs` is an ordered tuple of advisory canonical directives. Repairs are
|
|
299
|
+
never applied automatically; a host must explicitly submit a selected repair
|
|
300
|
+
through `engine.apply_directive(...)`.
|
|
301
|
+
|
|
302
|
+
The normative repair mapping is:
|
|
303
|
+
|
|
304
|
+
| Failure | Ordered advisory repairs |
|
|
305
|
+
| --- | --- |
|
|
306
|
+
| `PREMISE_ALREADY_SET` | `change premise to <requested value>` |
|
|
307
|
+
| `PREMISE_NOT_SET` | `set premise <requested value>` |
|
|
308
|
+
| `ITEM_PROHIBITED` | `remove policy <item>`; `use <item>` |
|
|
309
|
+
| `ITEM_ALREADY_IN_USE` | `remove policy <item>`; `prohibit <item>` |
|
|
310
|
+
| `REPLACEMENT_TARGET_PROHIBITED` | `remove policy <target>`; retry the original replacement directive |
|
|
311
|
+
| `REPLACEMENT_SOURCE_PROHIBITED` | no repair (`()`) |
|
|
312
|
+
| `REPLACEMENT_SOURCE_MISSING` | no repair (`()`) |
|
|
313
|
+
|
|
314
|
+
Repairs are canonical, ordered, and advisory only. The engine never applies
|
|
315
|
+
them automatically; hosts explicitly select and submit any repair they want to
|
|
316
|
+
use. `message` is presentation data, so control flow must use `failure` and
|
|
317
|
+
the structured directives rather than parse message text.
|
|
265
318
|
|
|
266
319
|
Meaning:
|
|
267
320
|
|
|
268
321
|
| kind | host behavior |
|
|
269
322
|
| --- | --- |
|
|
270
323
|
| no_directive | no canonical directive recognized; no authoritative state change; host decides what to do next |
|
|
271
|
-
| update |
|
|
272
|
-
| error | show `message
|
|
324
|
+
| update | canonical directive was accepted; inspect `changed` and use updated state downstream |
|
|
325
|
+
| error | canonical directive was rejected semantically; inspect `failure`, show `message`, and optionally offer `repairs` |
|
|
326
|
+
|
|
327
|
+
`engine.step(...)` is the raw input boundary: it parses user input, may return
|
|
328
|
+
`NoDirectiveDecision` when no canonical directive is produced, and performs
|
|
329
|
+
semantic evaluation only after canonical parsing succeeds. The canonical
|
|
330
|
+
execution boundary is `engine.apply_directive(...)`, which accepts only a
|
|
331
|
+
`CanonicalDirective` and returns only `UpdateDecision` or
|
|
332
|
+
`SemanticErrorDecision`.
|
|
273
333
|
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
instead of direct key traversal.
|
|
334
|
+
Grammar failures do not produce semantic errors. A semantic error is possible
|
|
335
|
+
only after a canonical directive has parsed successfully.
|
|
277
336
|
|
|
278
337
|
See [docs/api-reference.md](docs/api-reference.md) for the full public API
|
|
279
338
|
reference.
|
|
@@ -283,8 +342,8 @@ Common API entry points:
|
|
|
283
342
|
- engine lifecycle: `Engine()`, `engine.step(...)`,
|
|
284
343
|
`engine.premise`, `engine.policies`, `engine.export_json(...)`,
|
|
285
344
|
`engine.import_json(...)`
|
|
286
|
-
- decision
|
|
287
|
-
`
|
|
345
|
+
- decision variants: `NoDirectiveDecision`, `UpdateDecision`,
|
|
346
|
+
`SemanticErrorDecision`, `SemanticFailure`
|
|
288
347
|
- state transport: `engine.export_json(...)`, `engine.import_json(...)`
|
|
289
348
|
|
|
290
349
|
---
|
|
@@ -365,8 +424,8 @@ the compiler.
|
|
|
365
424
|
|
|
366
425
|
- They transport **authoritative state only**
|
|
367
426
|
- Hosts own any broader interaction or session workflow around that state
|
|
368
|
-
-
|
|
369
|
-
|
|
427
|
+
- Decision objects and advisory repairs are not persisted; persistence carries
|
|
428
|
+
authoritative state only
|
|
370
429
|
|
|
371
430
|
---
|
|
372
431
|
|
|
@@ -404,16 +463,20 @@ User: reset policies
|
|
|
404
463
|
User: clear state
|
|
405
464
|
```
|
|
406
465
|
|
|
407
|
-
Grammar invariant:
|
|
466
|
+
Grammar invariant: a single input never applies more than one canonical
|
|
467
|
+
directive.
|
|
408
468
|
Directive-shaped invalid input is outside the canonical language, and
|
|
409
469
|
`error` is reserved for canonical directives that later fail semantic
|
|
410
470
|
evaluation against authoritative state.
|
|
411
471
|
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
472
|
+
A semantic error is a terminal result for the current input. It leaves
|
|
473
|
+
authoritative state unchanged and returns the failure classification, failed
|
|
474
|
+
canonical directive, ordered advisory repairs, and presentation message.
|
|
475
|
+
Repairs are canonical directives. They are advisory only, are never applied
|
|
476
|
+
automatically, and require explicit host selection and submission through
|
|
477
|
+
`engine.apply_directive(...)`. An absent source item in a canonical replacement
|
|
478
|
+
directive is a semantic `error` and does not authorize degradation to plain
|
|
479
|
+
`use`.
|
|
417
480
|
|
|
418
481
|
Examples:
|
|
419
482
|
|
|
@@ -425,18 +488,25 @@ clear state
|
|
|
425
488
|
|
|
426
489
|
Invalid:
|
|
427
490
|
use docker and prohibit peanuts
|
|
428
|
-
set premise vegetarian and use docker
|
|
429
491
|
clear state then set premise new project
|
|
492
|
+
|
|
493
|
+
Premise payload (opaque):
|
|
494
|
+
set premise vegetarian and use docker
|
|
430
495
|
```
|
|
431
496
|
|
|
497
|
+
Policy compounds such as `use docker and prohibit peanuts` remain invalid;
|
|
498
|
+
premise `VALUE` is opaque and may contain directive-like words.
|
|
499
|
+
|
|
432
500
|
Quote behavior follows the current grammar literally:
|
|
433
501
|
|
|
434
502
|
```text
|
|
435
|
-
Passthrough:
|
|
503
|
+
Passthrough (`no_directive`):
|
|
436
504
|
"use docker and prohibit peanuts"
|
|
437
505
|
|
|
438
|
-
Invalid:
|
|
506
|
+
Invalid directive:
|
|
439
507
|
use "docker and prohibit peanuts"
|
|
508
|
+
|
|
509
|
+
Canonical directive (`set premise`):
|
|
440
510
|
set premise "use docker and prohibit peanuts"
|
|
441
511
|
```
|
|
442
512
|
|
|
@@ -461,8 +531,8 @@ boundary, see [DirectiveGrammarSpec.md](docs/DirectiveGrammarSpec.md).
|
|
|
461
531
|
**Isn't this just prompt reinjection?**
|
|
462
532
|
No. Prompt construction is one downstream use of authoritative state.
|
|
463
533
|
Context Compiler is the authority layer that decides when state changes are
|
|
464
|
-
allowed, when error is required, and
|
|
465
|
-
|
|
534
|
+
allowed, when a terminal error is required, and which advisory repairs are
|
|
535
|
+
available. For runnable application-layer examples, see
|
|
466
536
|
[`context-compiler-example-integrations`](https://github.com/rlippmann/context-compiler-example-integrations).
|
|
467
537
|
|
|
468
538
|
Human-facing interpretation is a separate concern. If you want to recognize
|
|
@@ -483,8 +553,8 @@ User: prohibit python_script
|
|
|
483
553
|
```
|
|
484
554
|
|
|
485
555
|
Without an authority layer, the application must invent conflict-resolution and
|
|
486
|
-
|
|
487
|
-
|
|
556
|
+
repair rules itself. Context Compiler applies deterministic state-transition
|
|
557
|
+
rules and can return a terminal error instead of silently
|
|
488
558
|
overwriting state.
|
|
489
559
|
|
|
490
560
|
---
|
|
@@ -493,11 +563,11 @@ overwriting state.
|
|
|
493
563
|
|
|
494
564
|
### Guarantees
|
|
495
565
|
|
|
496
|
-
- State changes only through
|
|
566
|
+
- State changes only through canonical directives that pass semantic evaluation.
|
|
497
567
|
- Identical input sequences produce identical compiler state.
|
|
498
568
|
- Model responses never modify compiler state.
|
|
499
569
|
- Ambiguous directives trigger error instead of changing state.
|
|
500
|
-
- Syntax errors never
|
|
570
|
+
- Syntax errors never produce semantic errors or state changes.
|
|
501
571
|
|
|
502
572
|
Behavioral tests and Hypothesis-based property tests verify these invariants.
|
|
503
573
|
|