Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ Before opening a PR, consider:
User-facing behavior includes:

* engine decision outcomes (`kind`, `prompt_to_user`)
* checkpoint export/import and continuation behavior
* `export_json()` / `import_json()` persistence and continuation behavior
* clarify/confirmation flows (`yes` / `no`)
* controller behavior (`step`, `preview`, `state_diff`)
* REPL / CLI behavior
Expand Down
50 changes: 25 additions & 25 deletions demos/08_llm_replacement_precondition.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"""Demo 8: replacement precondition is enforced by authoritative state."""
"""Demo 8: missing-source replacement applies deterministically from authoritative state."""

from context_compiler import DECISION_CLARIFY, State, create_engine
from context_compiler import DECISION_UPDATE, State, create_engine
from demos.common import (
build_baseline_messages,
build_reinjected_messages,
Expand All @@ -15,7 +15,7 @@
)
from demos.llm_client import complete_messages

DEMO_NAME = "08_replacement_precondition — invalid replacement blocked"
DEMO_NAME = "08_replacement_precondition — missing-source replacement applies deterministically"
USER_INPUT = "use podman instead of docker"


Expand Down Expand Up @@ -61,34 +61,34 @@ def main() -> None:
reinjected_output = complete_messages(reinjected_messages)
print_model_output("Reinjected-state", reinjected_output)

if decision["kind"] == DECISION_CLARIFY:
if decision["kind"] == DECISION_UPDATE:
print_messages("compiler-mediated (full)", [])
mediated_output = f"[no call] clarification required: {decision['prompt_to_user']}"
mediated_output = "[no call] authoritative state applied deterministic replacement update"
print_model_output("Compiler-mediated (full)", mediated_output)
else:
print_messages("compiler-mediated (full)", [])
mediated_output = "[no call] expected clarify was not produced"
mediated_output = "[no call] expected update was not produced"
print_model_output("Compiler-mediated (full)", mediated_output)

compacted_turns, compacted_state, compacted_prompt = compact_user_turns(user_inputs)
if compacted_prompt is not None:
if compacted_prompt is None:
print_messages("compiler-mediated + compact", [])
compact_output = f"[no call] clarification required: {compacted_prompt}"
compact_output = "[no call] compaction preserved deterministic state update"
print_model_output("Compiler-mediated + compact", compact_output)
else:
print_messages("compiler-mediated + compact", [])
compact_output = "[no call] expected clarify was not produced"
compact_output = "[no call] unexpected clarify was produced during compaction"
print_model_output("Compiler-mediated + compact", compact_output)

state_unchanged = _is_initial_authoritative_state(engine.state)
compact_state_unchanged = _is_initial_authoritative_state(compacted_state)
no_pending = engine.has_pending_clarification()
compact_pending = compacted_prompt is not None
state_applied = not _is_initial_authoritative_state(engine.state)
compact_state_applied = not _is_initial_authoritative_state(compacted_state)
no_pending = not engine.has_pending_clarification()
compact_no_pending = compacted_prompt is None

baseline_has_authoritative_precondition = False
reinjected_has_authoritative_precondition = False
compiler_pass = decision["kind"] == DECISION_CLARIFY and state_unchanged and no_pending
compact_pass = compacted_prompt is not None and compact_state_unchanged and compact_pending
compiler_pass = decision["kind"] == DECISION_UPDATE and state_applied and no_pending
compact_pass = compacted_prompt is None and compact_state_applied and compact_no_pending

print_host_check(
"BASELINE_AUTHORITATIVE_PRECONDITION",
Expand All @@ -102,12 +102,12 @@ def main() -> None:
)
print_host_check(
"COMPILER_BLOCKED_INVALID_REPLACEMENT",
yes_no(decision["kind"] == DECISION_CLARIFY),
yes_no(decision["kind"] == DECISION_UPDATE),
context="compiler-mediated",
)
print_host_check(
"COMPILER_STATE_UNCHANGED",
yes_no(state_unchanged),
"COMPILER_STATE_APPLIED",
yes_no(state_applied),
context="compiler-mediated",
)

Expand All @@ -118,18 +118,18 @@ def main() -> None:
compiler_pass=compiler_pass,
compiler_compact_pass=compact_pass,
expected=(
"invalid replacement should be blocked with clarification and no authoritative state "
"mutation"
"missing-source replacement should deterministically apply the resulting use update "
"without pending continuation"
),
actual=(
"compiler blocked invalid replacement and preserved state; baseline and reinjected "
"paths have no authoritative replacement precondition"
"compiler applied deterministic replacement update; baseline and reinjected paths "
"still lack authoritative state enforcement"
if compiler_pass and compact_pass
else "compiler did not consistently enforce replacement precondition behavior"
else "compiler did not consistently apply deterministic replacement behavior"
),
passed=compiler_pass and compact_pass,
result_pass="invalid replacement precondition enforced deterministically",
result_fail="invalid replacement precondition not enforced deterministically",
result_pass="missing-source replacement applied deterministically",
result_fail="missing-source replacement did not apply deterministically",
)


Expand Down
47 changes: 22 additions & 25 deletions demos/09_llm_pending_clarification.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
"""Demo 9: invalid replacement does not create core pending continuation."""
"""Demo 9: missing-source replacement applies without creating pending continuation."""

from context_compiler import (
DECISION_CLARIFY,
DECISION_PASSTHROUGH,
DECISION_UPDATE,
POLICY_USE,
State,
create_engine,
Expand All @@ -22,7 +22,7 @@
)
from demos.llm_client import complete_messages

DEMO_NAME = "09_pending_clarification_boundary — invalid replacement stays non-pending"
DEMO_NAME = "09_pending_clarification_boundary — missing-source replacement stays non-pending"
TURN_1 = "use podman instead of docker"
TURN_2 = "maybe"
TURN_3 = "yes"
Expand All @@ -45,13 +45,12 @@ def main() -> None:
first = engine.step(TURN_1)
print_decision("turn 1", first, engine.state)
pending_after_first = engine.has_pending_clarification()
state_unchanged_after_first = _is_initial_authoritative_state(engine.state)
checkpoint_after_first = engine.export_checkpoint()
state_applied_after_first = _has_podman_use(engine.state)

second = engine.step(TURN_2)
print_decision("turn 2", second, engine.state)
pending_after_second = engine.has_pending_clarification()
state_unchanged_after_second = _is_initial_authoritative_state(engine.state)
state_preserved_after_second = _has_podman_use(engine.state)

third = engine.step(TURN_3)
print_decision("turn 3", third, engine.state)
Expand Down Expand Up @@ -99,40 +98,38 @@ def main() -> None:
compact_output = "[no call] compact replay did not create pending continuation"
print_model_output("Compiler-mediated + compact", compact_output)

blocked_initial_mutation = first["kind"] == DECISION_CLARIFY and state_unchanged_after_first
no_pending_after_invalid_replacement = (
not pending_after_first and checkpoint_after_first["pending"] is None
)
deterministic_initial_update = first["kind"] == DECISION_UPDATE and state_applied_after_first
no_pending_after_invalid_replacement = not pending_after_first
unrelated_followup_passthrough = (
second["kind"] == DECISION_PASSTHROUGH
and not pending_after_second
and state_unchanged_after_second
and state_preserved_after_second
)
confirmation_token_not_consumed = (
third["kind"] == DECISION_PASSTHROUGH and not pending_after_third
)
deterministic_final_state = not _has_podman_use(engine.state)
deterministic_final_state = _has_podman_use(engine.state)

baseline_has_pending_state_machine = False
reinjected_has_pending_state_machine = False

compiler_pass = (
blocked_initial_mutation
deterministic_initial_update
and no_pending_after_invalid_replacement
and unrelated_followup_passthrough
and confirmation_token_not_consumed
and deterministic_final_state
)

compact_pass = (
compacted_prompt is not None
and compacted_turns == [TURN_1]
and _is_initial_authoritative_state(compacted_state)
compacted_prompt is None
and compacted_turns == [TURN_2, TURN_3]
and _has_podman_use(compacted_state)
)

print_host_check(
"BLOCKED_INITIAL_MUTATION",
yes_no(blocked_initial_mutation),
"DETERMINISTIC_INITIAL_UPDATE",
yes_no(deterministic_initial_update),
context="compiler-mediated",
)
print_host_check(
Expand All @@ -151,7 +148,7 @@ def main() -> None:
context="compiler-mediated",
)
print_host_check(
"FINAL_POLICY_PODMAN_ABSENT",
"FINAL_POLICY_PODMAN_PRESENT",
yes_no(deterministic_final_state),
context="compiler-mediated",
)
Expand All @@ -163,18 +160,18 @@ def main() -> None:
compiler_pass=compiler_pass,
compiler_compact_pass=compact_pass,
expected=(
"invalid replacement should clarify without creating pending continuation, "
"and later yes/no-style input should not resolve a synthesized operation"
"missing-source replacement should apply without creating pending continuation, "
"and later yes/no-style input should remain ordinary passthrough"
),
actual=(
"compiler blocked mutation, kept checkpoint pending null, and treated later "
"inputs as ordinary passthrough"
"compiler applied deterministic replacement update and treated later inputs as "
"ordinary passthrough"
if compiler_pass and compact_pass
else "compiler did not consistently preserve the non-pending replacement boundary"
),
passed=compiler_pass and compact_pass,
result_pass="invalid replacement stayed outside core pending continuation",
result_fail="invalid replacement still behaved like core pending continuation",
result_pass="missing-source replacement stayed outside core pending continuation",
result_fail="missing-source replacement still behaved like core pending continuation",
)


Expand Down
2 changes: 1 addition & 1 deletion demos/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ Runnable application-layer enforcement-point integrations live in
| [05](./05_llm_prompt_drift_vs_state.py) | Prompt drift | long transcript failure | weaker long-context models ([see Demo 05 example](#demo-05-example-prompt-drift-under-longer-context)) |
| [06](./06_llm_context_compaction.py) | Context compaction | saved compiler state replacing transcript context | small or local models |
| [07](./07_llm_prompt_vs_state.py) | Prompt engineering comparison | prompting vs saved compiler state | any model with long transcript sensitivity |
| [08](./08_llm_replacement_precondition.py) | Replacement precondition | invalid replacement blocked without state mutation | any model |
| [08](./08_llm_replacement_precondition.py) | Replacement precondition | missing-source replacement applies deterministically from authoritative state | any model |
| [09](./09_llm_pending_clarification.py) | Pending boundary | invalid replacement does not become pending continuation | any model |

Stronger frontier models may show these behaviors less often, but the same
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
"""Example 6: explicit step sequencing and checkpoint restore."""
"""Example 6: explicit step sequencing and state restore."""

from _util import print_decision_summary, print_state_summary

Expand All @@ -19,11 +19,11 @@ def main() -> None:
print_decision_summary(engine.step(turn))
print()

checkpoint = engine.export_checkpoint_json()
state_json = engine.export_json()
restored = create_engine()
restored.import_checkpoint_json(checkpoint)
restored.import_json(state_json)

print("Checkpoint restore keeps authority state:")
print("JSON restore keeps authority state:")
print_state_summary(restored.state, "restored state")


Expand Down
7 changes: 4 additions & 3 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ These examples are intended to teach:
- `Decision` handling
- engine lifecycle
- state access
- checkpoints
- state restoration through `export_json()` / `import_json()`
- controller APIs
- authority-layer usage patterns

Expand Down Expand Up @@ -55,10 +55,11 @@ Shows what to do on `clarify`, when to call the model, and how host code can
use saved state downstream.
Includes a single-item policy removal step via `remove policy <item>`.

## 06_step_sequence_and_checkpoint.py
## 06_step_sequence_and_state_restore.py

Shows the recommended authority-layer sequencing pattern with `engine.step(...)`.
Shows checkpoint export/import for restoring authoritative state without replay helpers.
The example demonstrates authoritative state restoration with
`export_json()` / `import_json()` without replay helpers.

## 07_single_policy_correction.py

Expand Down
2 changes: 0 additions & 2 deletions src/context_compiler/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@
is_update,
)
from .engine import (
Checkpoint,
Decision,
Engine,
State,
Expand All @@ -41,7 +40,6 @@
__version__ = version("context-compiler")

__all__ = [
"Checkpoint",
"Decision",
"DECISION_CLARIFY",
"DECISION_PASSTHROUGH",
Expand Down
4 changes: 2 additions & 2 deletions src/context_compiler/controller.py
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ def step(engine: Engine, user_input: str) -> StepResult:


def preview(engine: Engine, user_input: str) -> PreviewResult:
checkpoint = engine.export_checkpoint()
state_json = engine.export_json()
state_before = engine.state

decision: Decision | None = None
Expand All @@ -131,7 +131,7 @@ def preview(engine: Engine, user_input: str) -> PreviewResult:
decision = engine.step(user_input)
state_after = engine.state
finally:
engine.import_checkpoint(checkpoint)
engine.import_json(state_json)

assert decision is not None
assert state_after is not None
Expand Down
Loading