Private
Public Access
2.7 KiB
2.7 KiB
Context must be ordered stable-to-volatile — the stable prefix (layers 1-7) gets cached, the volatile suffix (layers 8-12) never does
1. The 12-layer model (the stable-to-volatile ordering)
| # | Layer | Stable across turns? | Source | SSDL |
|---|---|---|---|---|
| 1 | Role instructions (model + provider) | yes | _get_combined_system_prompt |
[I] |
| 2 | Function-calling schema | yes | per provider | [I] |
| 3 | Discovered tool descriptions | yes | mcp_client.get_tool_schemas() |
[I] |
| 4 | System prompt preset | yes | app_state.ai_settings.system_prompt |
[I] |
| 5 | Persona profile | yes | app_state.active_persona |
[I] |
| 6 | Project context (per manual_slop.toml) |
yes | NEW (Candidate 14) | [I] |
| 7 | Knowledge digest (per knowledge/digest.md) |
yes (within a gc cycle) | NEW (Candidate 8) | [I] |
| 8 | Discussion metadata (name, role count) | no (per turn) | disc_entries[:1] or disc_meta |
─── (data) |
| 9 | Active preset (FileItem set) | no (per turn) | self.context_files |
─── (data) |
| 10 | Per-file details (history, slices, notes) | no (per file) | per FileItem |
─── (data) |
| 11 | Tool-call results from prior turns | no (per turn) | per _reread_file_items |
─── (data) |
| 12 | The user message | no (per turn) | the input | ─── (data) |
The cache boundary is at layer 7/8. Layers 1-7 are byte-identical across turns of the same discussion (and across discussions of the same mode). Layers 8-12 change per turn.
2. The byte-comparison test (the design contract)
The design rule "stable prefix is byte-identical" must be testable. The test:
# In tests/test_aggregate_caching.py (NEW)
def test_aggregate_stable_to_volatile_ordering():
"""The first N characters of the context should be identical across turns
of the same conversation, when no stable-layer inputs change."""
ctrl = mock_app_controller()
ctrl.ai_settings.system_prompt = "Test system prompt"
ctrl.active_persona = mock_persona()
# Turn 1
turn1 = aggregate.build_initial_context(ctrl, user_message="first prompt")
# Turn 2 (same stable inputs, different user message)
turn2 = aggregate.build_initial_context(ctrl, user_message="second prompt")
# The first N characters should be identical (N = where the volatile layers start)
N = aggregate.stable_prefix_length(ctrl)
assert turn1[:N] == turn2[:N], f"Stable prefix mismatch: {turn1[:N]!r} != {turn2[:N]!r}"
The test is the contract. If a new layer is added in the middle of the stack, this test fails; the agent must either move the layer to the stable position or update the test (with written justification).