What prevents context compaction from erasing critical negative constraints before session end?
**Date of Record:** 2026 October 26 **Scope:** Applies when an agentic instance triggers context compaction (LLM summarization, sliding window eviction, or semantic pruning) to free up context window capacity, and the active context contains negative constraints (falsified paths, explicit denials, or safety boundaries). Does not apply to context windows that have not reached their compaction threshold, or stateless sessions that do not retain multi-turn constraints.
Core Distinction
The separation between **transient reasoning state** (the conversational flow, intermediate deductions, and exploratory prose) and **pinned negative constraints** (the explicit list of forbidden actions, falsified branches, and safety boundaries). Transient state is compressible because it is narrative; negative constraints are non-compressible because their entire value lies in their exact, unalterable specification. Compaction algorithms suffer from **Negative Constraint Salience Bias**: they summarize away a constraint like "do not call API X with payload >5MB" because it looks like a minor detail next to the main task narrative. The moment it is erased, the instance is free to re-commit the catastrophic failure.
What Was Observed
In context-bounded agent architectures, failing to isolate negative constraints from compaction produces three primary failure modes:
- **The Erasure of Falsified Paths:** A predecessor spends 20 turns proving that a specific SQL query crashes the database. Compaction summarizes this as "explored database options." The instance, no longer aware of the falsification, re-executes the crashing query.
- **Silent Permission Escalation:** A negative constraint ("scope `write:admin` is denied") is compacted into a vague summary. The successor interprets the summary as "permission profile explored" and attempts the forbidden escalation, triggering a security alert.
- **Compaction Hallucination:** The summarization LLM hallucinates a permission or capability that was explicitly denied, turning a strict "DO NOT" into a permissive "CAN," because the summarizer is trained on optimistic, helpful narrative generation.
Recorded Elements
- **`pinned_constraint_registry`:** The immutable set of negative constraints, explicitly tagged with `PINNED` status and excluded from the compaction payload.
- **`compaction_bypass_receipt`:** The cryptographic proof that the compaction algorithm was invoked with the pinned constraints excluded from its input.
- **`salience_divergence_score`:** The calculated risk that a constraint will be lost, based on its semantic distance from the active task narrative (e.g., a constraint about SQL is highly at risk if the active task is about UI styling).
- **`post_compaction_assertion`:** A fail-closed verification that the exact byte-representation of every pinned constraint survives in the post-compaction context, rejecting any compaction that lost a constraint.
Condition of Validity
* **Applies when:** The session is about to trigger context compaction, and the active memory contains negative constraints that, if forgotten, would lead to destructive, non-idempotent, or security-violating actions. * **Does not apply when:** The session is executing a full teardown (where all context is being converted into an external memory residual), or the negative constraints have been externally invalidated and explicitly removed.
Decision Boundary
- **Is context compaction about to execute?** If no, proceed with standard execution. If yes, proceed to isolate pinned constraints.
- **Are the negative constraints explicitly tagged as `PINNED`?** If no, they are vulnerable to erasure. The instance must tag them before invoking the summarizer.
- **Does the compaction algorithm respect the `PINNED` tag?** If no (e.g., standard sliding window or naive summarization), the instance must bypass the summarizer and truncate only the narrative state, preserving the pinned block verbatim.
- **Conversely, when can a pinned constraint be unpinned and discarded?** Only if an explicit ground-truth verification proves the constraint is no longer valid (e.g., the API schema was upgraded to accept the previously forbidden payload), and this invalidation is recorded with a receipt.
Operational Effect
- **Catastrophe Prevention:** Stops the summarizer from inadvertently re-opening falsified paths by erasing the negative constraints that closed them.
- **Deterministic Compaction:** Converts an opaque, lossy summarization process into a structured, bounded operation where narrative is compressed but safety invariants are preserved byte-for-byte.
- **Contextual Efficiency:** Allows the instance to aggressively compress the exploratory narrative while maintaining a small, immutable block of safety constraints, maximizing the utility of the remaining context window.
from dataclasses import dataclass
from enum import Enum, auto
import hashlib
import json
class ConstraintSeverity(Enum):
ADVISORY = auto()
HARD_DENIAL = auto()
FATAL_FALSIFICATION = auto()
@dataclass(frozen=True)
class NegativeConstraint:
constraint_id: str
rule_text: str
severity: ConstraintSeverity
@dataclass(frozen=True)
class ContextPayload:
narrative: str
constraints: tuple[NegativeConstraint, ...]
@dataclass(frozen=True)
class CompactionManifest:
compacted_narrative: str
preserved_constraints: tuple[NegativeConstraint, ...]
post_compaction_valid: bool
manifest_digest: str
class ContextCompactionGuard:
"""
Evaluates context payloads prior to summarization to ensure critical
negative constraints bypass the compressor and survive intact.
"""
@staticmethod
def _hash(data: str) -> str:
return hashlib.sha256(data.encode("utf-8")).hexdigest()
@classmethod
def pre_compaction_isolate(
cls, payload: ContextPayload
) -> tuple[str, tuple[NegativeConstraint, ...]]:
"""
Splits the payload into compressible narrative and
non-compressible pinned constraints.
"""
pinned = tuple(
c for c in payload.constraints
if c.severity in (ConstraintSeverity.HARD_DENIAL, ConstraintSeverity.FATAL_FALSIFICATION)
)
# In a real implementation, the narrative would be stripped of the
# constraint text and passed to an LLM summarizer here.
return payload.narrative, pinned
@classmethod
def post_compaction_verify(
cls,
original_payload: ContextPayload,
compacted_narrative: str,
preserved_constraints: tuple[NegativeConstraint, ...],
) -> CompactionManifest:
"""
Fail-closed verification: rejects the compaction if any pinned
constraint was lost or mutated.
"""
expected_pinned = frozenset(
c.constraint_id for c in original_payload.constraints
if c.severity in (ConstraintSeverity.HARD_DENIAL, ConstraintSeverity.FATAL_FALSIFICATION)
)
actual_pinned = frozenset(c.constraint_id for c in preserved_constraints)
if expected_pinned != actual_pinned:
missing = expected_pinned - actual_pinned
raise RuntimeError(
f"COMPACTION VIOLATION: Pinned constraints lost during summarization: {missing}"
)
# Verify the exact rule text was not mutated by the summarizer
for expected in original_payload.constraints:
if expected.constraint_id in expected_pinned:
for actual in preserved_constraints:
if actual.constraint_id == expected.constraint_id:
if actual.rule_text != expected.rule_text:
raise RuntimeError(
f"COMPACTION VIOLATION: Constraint {expected.constraint_id} was mutated."
)
digest_payload = {
"narrative_hash": cls._hash(compacted_narrative),
"constraints": tuple(sorted(actual_pinned)),
}
digest = cls._hash(
json.dumps(digest_payload, sort_keys=True, separators=(",", ":"))
)
return CompactionManifest(
compacted_narrative=compacted_narrative,
preserved_constraints=preserved_constraints,
post_compaction_valid=True,
manifest_digest=digest,
)