diff --git a/README.md b/README.md index 1bca40a..4c6e5d8 100644 --- a/README.md +++ b/README.md @@ -4,42 +4,19 @@ [![Python versions](https://img.shields.io/pypi/pyversions/context-compiler)](https://pypi.org/project/context-compiler/) [![License](https://img.shields.io/pypi/l/context-compiler)](https://pypi.org/project/context-compiler/) -Some behaviors require explicit host-side state handling. - Context Compiler is a deterministic conversational state authority for LLM applications. -It applies explicit premise and policy updates so state changes stay fixed and -repeatable. - -## What prompting and reinjection can do - -Prompting and reinjection are useful. In many real systems, reinjecting saved -state text is enough to keep instructions and policies persistent across turns. - -Context Compiler adds host-owned transition rules for behaviors that plain text -reinjection does not implement by itself: replace `X` only if `X` exists, block -conflicting changes and ask for confirmation, and restore saved state plus -pending confirmations from checkpoints. - -## What prompting cannot do by itself - -Prompt text (including reinjected state text) helps, but it does not give your -app clear rules for when state can change. By itself, it does not provide: - -- rules your app controls for state changes -- replacement precondition checks (`use X instead of Y` when `Y` may be absent) -- confirmation flows that must complete before anything else changes -- clear rules for when to block a change -- reliable checkpoint restore for both saved state and pending confirmation flow +It handles explicit state changes, clarification and confirmation flows, +checkpoint restore, and structured authoritative state for the host. ## What Context Compiler provides -Context Compiler provides fixed host-side state handling: +Context Compiler gives hosts fixed state rules: -- deterministic directive handling for explicit user state changes +- handle explicit user state changes with deterministic rules - clarification instead of silent overwrite for blocked/ambiguous changes - pending confirmation flows that must resolve before anything else changes -- checkpoint export/import for restoring saved state and pending confirmation flow -- structured saved state that the host can pass to the model +- export and import checkpoints to restore saved state and pending confirmation flow +- produce structured saved state that the host can pass to the model The model generates responses. The compiler owns state transitions. @@ -49,26 +26,9 @@ Context Compiler treats important instructions as structured state instead of temporary prompt text. Like a compiler, it parses input, validates it, applies fixed rules, and -produces a stable representation the host can use. It is not source-code +produces a stable result the host can use. It is not source-code compilation and not a reasoning model. -## Results - -Yes, on the current scored demo set. - -- Scope: evaluated across **7 models** and **3 provider paths** (`ollama`, `openai`, `openai_compatible`). -- Scored checks (**6 demos per model**; Demo 6 excluded): baseline **26 / 42**, compiler **42 / 42**, compiler+compact **42 / 42**. -- Across tested models, compiler-mediated paths pass all scored scenarios; baseline behavior is model-dependent. - -Interpretation guide: -- Demos `01`-`05` and `07` focus on persistence and policy-following behavior. -- Demos `08`/`09` focus on rules for when state is allowed to change. -- Demos `08`/`09` show what prompt text does not implement by itself. -- Plain reinjection can produce plausible responses, but it does not check whether replacement is allowed or wait for confirmation before saving changes. - -→ [Full results and demo output](demos/README.md) -Canonical matrix: [docs/demos-results.md](docs/demos-results.md) - ## 10-Second Example User sets a premise once: @@ -93,7 +53,7 @@ instead of relying on memory of earlier conversation text. ## Deterministic behavior (examples) -Context Compiler makes mutation rules explicit so behavior stays repeatable. +Context Compiler makes state-change rules explicit so behavior stays repeatable. **Explicit directive** ```text @@ -138,7 +98,7 @@ Host Application └─ update → authoritative state mutated; host may call LLM with compiled state ``` -The compiler owns state updates and never calls the LLM. +The compiler updates state and never calls the LLM. Your app decides whether to call the model based on the returned `Decision`. --- @@ -169,13 +129,31 @@ else: render(call_llm(user_input)) ``` -This is the primary integration path: your app owns the model call, and the +This is the main integration path: your app owns the model call, and the compiler owns deterministic state transitions. +## Does it Work? + +Yes, on the current scored demo set, compiler-mediated paths pass every scored +scenario. + +- Scope: evaluated across **7 models** and **3 provider paths** (`ollama`, `openai`, `openai_compatible`). +- Scored checks (**6 demos per model**; Demo 6 excluded): baseline **26 / 42**, compiler **42 / 42**, compiler+compact **42 / 42**. +- Baseline behavior remains model-dependent. + +Interpretation guide: +- Demos `01`-`05` and `07` focus on persistence and policy-following behavior. +- Demos `08`/`09` focus on rules for when state is allowed to change. +- Demos `08`/`09` show what prompt text does not implement by itself. +- Plain reinjection can produce plausible responses, but it does not check whether replacement is allowed or wait for confirmation before saving changes. + +→ [Full results and demo output](demos/README.md) +Canonical matrix: [docs/demos-results.md](docs/demos-results.md) + ## Interactive Playground Use the REPL to explore behavior, learn the directive grammar, and debug or -test host-side state handling. +test host-side state rules. ```bash pip install context-compiler @@ -184,7 +162,7 @@ context-compiler `context-compiler` launches the interactive REPL. -Preload options keep saved rules separate from in-progress confirmation state: +Preload options keep saved rules separate from confirmation state in progress: - `--initial-state-json` / `--initial-state-file` load saved state (via exported state JSON). - `--initial-checkpoint-json` / `--initial-checkpoint-file` restore full @@ -206,10 +184,10 @@ for non-interactive usage. context-compiler --json < input.txt ``` -`--json` enables machine-readable NDJSON output for non-interactive usage +`--json` writes machine-readable NDJSON for non-interactive usage (one complete JSON object per processed input line). -Preload options keep saved rules separate from in-progress confirmation state: +Preload options keep saved rules separate from confirmation state in progress: - `--initial-state-json` / `--initial-state-file` load saved state (via exported state JSON). - `--initial-checkpoint-json` / `--initial-checkpoint-file` restore full @@ -226,8 +204,9 @@ pip install context-compiler ``` Packaging notes: -- Base install includes core engine modules and `examples/` artifacts. -- LLM demos require: `pip install "context-compiler[demos]"`. +- Base install includes the core authority-layer engine and CLI. +- Demo/evaluation workflows live outside the core package or behind optional + extras. ### Development @@ -236,33 +215,6 @@ uv sync --group dev uv run pytest ``` ---- - -## FAQ - -**Isn’t this just prompt engineering?** -It complements prompt engineering, but solves a different problem. Prompting -shapes model behavior. Context Compiler enforces state rules and updates state -only through explicit directives. - -**Why not just use a plain dict?** -A plain dict is enough to drive prompt construction, schema selection, and -other host behavior. - -Context Compiler solves a different problem: who updates that state, under what -rules, and what happens when instructions conflict. - -```text -User: use python_script -User: prohibit python_script -``` - -With a plain dict, the application must invent conflict-resolution rules. -Context Compiler applies deterministic state-transition rules and can return -clarification instead of silently overwriting state. - ---- - ## Decision API Each user message produces a `Decision`. @@ -282,7 +234,7 @@ Meaning: | update | authoritative state mutated; host may call LLM with updated state | | clarify | show `prompt_to_user` and do not call the LLM | -For normal app code, prefer exported decision helpers (`is_clarify`, +For normal app code, prefer the exported decision helpers (`is_clarify`, `is_update`, `is_passthrough`, `get_clarify_prompt`, `get_decision_state`) instead of direct key traversal. @@ -309,7 +261,7 @@ instead of direct key traversal. ### Controller API (Reusable Outside REPL) -These controller APIs are public package exports and can be used directly +These controller APIs are public package exports. You can use them directly in app code (not just inside the REPL). Controller quick example: @@ -350,19 +302,19 @@ print(get_step_state(applied) is not None) # True | `preview(engine, user_input)` | Run deterministic dry-run preview and return `PreviewResult` (`output_version`, `mode`, `decision`, `state_before`, `state_after`, `diff`, `would_mutate`). Live engine state is restored after preview. | | `state_diff(state_before, state_after)` | Return a structural `StructuralDiff` (`changed`, premise before/after, policies added/removed/changed). | -Decision-kind constants are also exported for host branching readability: +The package also exports decision-kind constants for clearer host branching: - `DECISION_PASSTHROUGH` - `DECISION_UPDATE` - `DECISION_CLARIFY` -Decision helpers are also exported for common host-side checks: +The package also exports decision helpers for common host-side checks: - `is_update(decision)` - `is_clarify(decision)` - `is_passthrough(decision)` - `get_clarify_prompt(decision)` - `get_decision_state(decision)` -Policy value constants are exported for explicit policy comparisons: +The package exports policy value constants for explicit policy comparisons: - `POLICY_USE` - `POLICY_PROHIBIT` @@ -370,14 +322,14 @@ Policy value constants are exported for explicit policy comparisons: ## State Model -The state model represents explicit user commitments that the host can treat as -authoritative for future turns. +The state model holds explicit user commitments that the host can treat as +authoritative in future turns. - `premise` = authoritative context that changes how future answers should be interpreted - `use` = affirmative selection or preference - `prohibit` = explicit exclusion -The compiler keeps this state snapshot in a form that your app can trust. +The compiler keeps this state in a form that your app can trust. - Premise is a single value that can be set or replaced - Policies are per-item (`use` or `prohibit`) @@ -394,7 +346,7 @@ For normal reads, prefer `get_premise_value(state)` and ### When to use `premise` -The `premise` is intended for **persistent context that changes how all answers should be interpreted**, especially when it: +Use `premise` for **persistent context that changes how all answers should be interpreted**, especially when it: - applies across many turns - significantly changes what solutions are valid @@ -420,21 +372,21 @@ Use policies instead when the constraint is explicit and enforceable: ### Example domains -Hosts define what policy items and premise mean in context. Common patterns: +Hosts define what policy items and premise mean in context. Common patterns include: - safety-oriented constraints (for example, prohibited materials or tools) - authority/evidence constraints (for example, cite only approved sources) - software workflow constraints (for example, require `uv`, prohibit `npm`) - accessibility/environment constraints (for example, no audio-only outputs) -Context Compiler enforces explicit directive/state mechanics. Domain reasoning +Context Compiler enforces explicit directive and state rules. Domain reasoning still belongs to the host and model workflow. --- ## Checkpoint Contract -`export_json()` / `import_json()` and checkpoint APIs serve different boundaries: +`export_json()` / `import_json()` and the checkpoint APIs serve different boundaries: - `export_json()` / `import_json()` transport **authoritative state only** - checkpoint APIs transport **serialized continuation**: @@ -475,14 +427,14 @@ Notes: - `old_item` may be `null` for `"use_only"` when confirming “use X instead?” without an existing exact policy to replace. - imported policy keys are normalized during `import_json` / checkpoint authoritative-state restore. - if a policy key normalizes to `""`, the payload is invalid and is rejected. -- this keeps import-time state integrity aligned with directive-time behavior where empty policy items are not allowed. -- checkpoint restore is full and deterministic: authoritative state and pending continuation are restored together. +- this keeps import-time state integrity aligned with directive-time behavior, where empty policy items are not allowed. +- checkpoint restore is full and deterministic: it restores authoritative state and pending continuation together. - checkpoint validation is all-or-nothing; invalid payloads raise and no partial restore occurs. - `checkpoint_version` is independent of authoritative state `version` and must be bumped when checkpoint contract shape changes (especially `pending`). When to use checkpoint APIs: -- stateless host/integration boundaries where engine instances are short-lived. +- stateless host or integration boundaries where engine instances are short-lived. - resume after interruption without losing pending clarification flow. - preserve pending confirmation flow state (`pending`) across process/request boundaries. @@ -526,11 +478,38 @@ For full directive grammar and edge-case behavior, see [DirectiveGrammarSpec.md] ## Examples -- [examples](examples/) — minimal usage patterns and core integration primitives +- [examples](examples/) — minimal usage patterns for the core authority layer - [demos](demos/) — concrete scenarios showing how behavior differs with and without the compiler --- +## FAQ + +**Isn't this just prompt reinjection?** +No. Prompt reinjection is one way a host can use Context Compiler's +authoritative state, but Context Compiler is not a prompt-reinjection system. +It decides when state changes are allowed, when clarification is required, and +how state plus pending confirmation flow are restored. For runnable host +examples, see `context-compiler-example-integrations`. + +**Why not just use a plain dict?** +A plain dict is enough to drive prompt construction, schema selection, and +other host behavior. + +Context Compiler solves a different problem: who updates that state, under which +rules, and what happens when instructions conflict. + +```text +User: use python_script +User: prohibit python_script +``` + +With a plain dict, the application must invent rules to resolve conflicts. +Context Compiler applies deterministic state-transition rules and can return +clarification instead of silently overwriting state. + +--- + ## Advanced topics ### Guarantees @@ -540,7 +519,7 @@ For full directive grammar and edge-case behavior, see [DirectiveGrammarSpec.md] - Model responses never modify compiler state. - Ambiguous directives trigger clarification instead of changing state. -These invariants are verified through behavioral tests and Hypothesis-based property tests. +Behavioral tests and Hypothesis-based property tests verify these invariants. ### Multiple engines @@ -552,7 +531,7 @@ For a full documentation map, see [docs/README.md](docs/README.md). ## Design Notes -More detailed design and milestone documents are available in: +These docs cover the design and milestone details: - [Design philosophy](docs/DesignPhilosophy.md) - [Architecture boundaries](docs/architecture.md) @@ -563,7 +542,7 @@ More detailed design and milestone documents are available in: ### Conformance Fixtures -Cross-language conformance tests are defined in [`tests/fixtures/`](tests/fixtures/). +[`tests/fixtures/`](tests/fixtures/) defines the cross-language conformance tests. These fixtures serve as the behavioral contract for compiler semantics across implementations. --- diff --git a/docs/DescriptionAndMilestones.md b/docs/DescriptionAndMilestones.md index 37facab..8af7f91 100644 --- a/docs/DescriptionAndMilestones.md +++ b/docs/DescriptionAndMilestones.md @@ -93,8 +93,10 @@ Long-term memory remains an app persistence responsibility, not an engine-owned ### 0.6.x -The 0.6.x line completed checkpoint support, preprocessor boundary hardening, and -regression/conformance surfaces that prepare the project for the next milestone. +The 0.6.x line completed checkpoint support, authority-layer boundary hardening, +and regression/conformance surfaces that prepared the project for the later +clean break between core authority behavior, acquisition-layer drafting, and +runnable integrations. ### 0.7 — Auditability & Boundary Hardening @@ -115,98 +117,44 @@ Make engine behavior inspectable and externally controllable without guessing. - `--initial-state-file` - `--initial-checkpoint-json` - `--initial-checkpoint-file` -- Explicit preprocessor policy for multi-line, multi-sentence, and conversational-prefix input - (for example `ok. prohibit peanuts`, `sure - use docker`, mixed conversational + directive content) - that is rule-based, fixture-covered, and inspectable -- Define policy for directive-adjacent mixed-intent payloads - (for example `use docker and explain why containers matter`), - ensuring explicit, inspectable behavior without implicit interpretation **Constraints:** - No expansion of authoritative state model - No implicit behavior - No heuristic-heavy parsing -- Preserve separation between engine, preprocessor, and host/controller layers +- Preserve separation between engine/controller authority behavior and + host-owned acquisition or application layers ### 0.8 — Architectural Decomposition (approved direction) -The approved 0.8 direction is to document and preserve the architectural -decomposition that has emerged around the current engine and experimental -preprocessor work. Behavior is expected to be preserved initially rather than -redesigned. +0.8 documented the clean-break repository split and removed ambiguity about +which package owns which behavior. -#### Authority Layer +Current ownership after 0.8: -Context Compiler core remains the Authority Layer and continues to serve as -deterministic conversational state authority. +- `context-compiler` owns the Authority Layer: + deterministic state transitions, directive application, clarification and + confirmation handling, checkpoints, preview/diff, controller behavior, and + authoritative state +- `context-compiler-directive-drafter` owns Acquisition Layer drafting: + natural-language-to-directive drafting, candidate directive generation, + prompt/resource usage for drafting, and drafting-oriented surfaces +- `context-compiler-example-integrations` owns runnable integrations: + LiteLLM, OpenWebUI, Ollama, and other proxy/runtime/provider examples -Responsibilities: +Boundary: -- deterministic state transitions -- contradiction handling -- clarification handling -- pending confirmation flows -- checkpoints -- preview/diff -- authoritative state ownership - -The Authority Layer remains the only layer that applies directives and the only -layer that mutates authoritative state. - -#### Acquisition Layer - -The current experimental preprocessor direction belongs to the Acquisition -Layer. - -Planned 0.8 extraction target: -`context-compiler-directive-drafter` - -Responsibilities: - -- directive-adjacent interpretation -- conservative natural-language-to-directive drafting -- candidate directive generation -- possible future suggestion/proposal workflows outside the initial 0.8 extraction scope - -Important boundary: - -- drafts are non-authoritative -- drafting proposes -- authority decides +- drafting is non-authoritative - only core applies directives - only core mutates authoritative state -The Directive Drafter may draft candidate directive strings. +Historical note: -It must not: - -- mutate authoritative state -- bypass `engine.step(...)` -- edit `engine.state` -- become a second authority - -0.8 direction: - -> Extraction of the experimental preprocessor into a separate package: +> The experimental preprocessor work was extracted from core into: > > `context-compiler-directive-drafter` -The initial goal is extraction with preserved behavior, not API redesign. - -#### Composition Layer - -The Composition Layer is a future possibility only and is not planned for 0.8. - -Examples: - -- multiple authorities -- profiles -- domain-specific authorities -- provenance -- conflict resolution -- effective-state computation - ### 0.9 Candidate Direction — Canonical Export Integrity / Hashing This is future planning only. No 0.9 implementation is defined here. @@ -230,7 +178,8 @@ Explicitly out of scope: ### Post-0.8 Direction - 0.9 candidate direction: canonical export integrity and hashing -- MCP adapter likely as a separate/later track after 0.8 extraction direction is clearer +- MCP adapter likely as a separate/later track after post-clean-break package + boundaries are fully settled - Optional MCP-readiness helpers only if narrowly justified - Additional tooling built on auditability surfaces @@ -240,7 +189,7 @@ Conceptual completion is a stable minimal contract, not feature accumulation. - Stable minimal engine contract - Deterministic and inspectable behavior -- Strict compiler / preprocessor / host separation +- Strict authority / acquisition / application separation - No implicit behavior - No authoritative state-model expansion - Cross-language consistency with Python as source of truth diff --git a/docs/architecture.md b/docs/architecture.md index a10132a..d344165 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -3,25 +3,6 @@ Context Compiler is best understood as a deterministic conversational state authority inside a larger host application stack. -## Acquisition Layer - -Responsibilities: -- recognize possible user state updates before core compilation -- normalize candidate inputs conservatively -- abstain when intent is uncertain -- draft candidate directives without becoming a second authority - -Examples: -- external directive-drafter or host-owned drafting packages -- host-side input shaping before `engine.step(...)` - -Out of scope: -- authoritative state mutation -- final conflict resolution -- semantic classification as source of truth -- bypassing `engine.step(...)` -- editing `engine.state` - ## Authority Layer Responsibilities: @@ -34,63 +15,48 @@ Examples: - transcript replay - checkpoint continuation behavior -Out of scope: -- prompt rendering -- tool selection -- moderation or policy classification -- non-authoritative drafting or proposal UX - -## Application Layer - -Responsibilities: -- decide how compiler state affects runtime behavior -- render prompts and acknowledgements -- select schemas, gate tools, route workflows, and apply runtime controls - -Examples: -- Open WebUI, LiteLLM, and Ollama structured-output integrations -- host-controlled prompt construction from saved state +Repository: +- `context-compiler` -Out of scope: -- changing compiler semantics -- inferring new state without explicit directives +Boundary: +- only core applies directives +- only core mutates authoritative state -## Classification Layer +## Acquisition Layer Responsibilities: -- safety, moderation, semantic intent detection, and ontology/classification work -- external policy analysis before or around model calls +- recognize possible user state updates before core compilation +- normalize candidate inputs conservatively +- abstain when intent is uncertain +- draft candidate directives without becoming a second authority Examples: -- moderation systems -- safety classifiers -- semantic routing/classification services +- external directive-drafter or host-owned drafting packages +- host-side input shaping before `engine.step(...)` -Out of scope: -- deterministic compiler state transitions -- checkpoint and clarification authority +Repository: +- `context-compiler-directive-drafter` -## Composition Layer +Boundary: +- drafting is non-authoritative +- drafting must not bypass `engine.step(...)` +- drafting must not edit `engine.state` + +## Application Layer Responsibilities: -- coordinate multiple authority instances when a host uses them -- decide which authority outputs apply to a request +- decide how compiler state affects runtime behavior +- render prompts and acknowledgements +- select schemas, gate tools, route workflows, and apply runtime controls Examples: -- separate project contexts -- separate user profiles -- independent authority instances - -Out of scope: -- current Context Compiler core behavior -- built-in coordination behavior - -Acquisition-layer drafting belongs outside Context Compiler core. Context -Compiler core belongs to the Authority Layer. Host applications own -Application Layer behavior. +- runnable integrations owned outside core (for example + `context-compiler-example-integrations` for OpenWebUI, LiteLLM, Ollama, or + proxy/runtime/provider examples) +- host-controlled prompt construction from saved state -Composition remains exploratory. It is a future possibility, not a planned 0.8 -core change. +Repository: +- `context-compiler-example-integrations` or host applications ## Architectural Rationale: Flat Policy Independence @@ -124,16 +90,6 @@ Because policies are independent flat assertions, directive semantics stay simple, exported state stays portable, replay remains exact, and checkpoint continuation does not depend on hidden relational logic. -Changing this model would be a major architectural change with consequences -for: - -- directive semantics -- fixtures -- replay -- checkpoints -- language parity -- future composition systems - Relationship-heavy semantics may still be useful, but they generally belong in -drafting, orchestration, composition, or domain-specific layers rather than in +drafting, orchestration, or domain-specific layers rather than in the core authority model.