fix: harden salvaged preflight display rollback for test-double density

Follow-up to the #54805 cherry-pick (skill guard rules):
- turn_context.py: snapshot_preflight_display_tokens gets a
  getattr+callable guard (SimpleNamespace compressor doubles / plugin
  context engines lack the ContextCompressor-only method) and the
  snapshot value is type-pinned to a real int (bool excluded) before
  arming the rollback — MagicMock compressors return truthy Mocks.
- turn_finalizer.py: interrupted pinned 'is True', the
  _turn_received_provider_response read pinned 'is not True' (MagicMock
  auto-attrs are truthy), and the compressor rollback method call gets a
  getattr+callable guard. Rollback stays display-only: it never touches
  _ineffective_compression_count or any durable guard, and preserves the
  -1 post-compaction sentinel.
This commit is contained in:
Teknium 2026-07-23 11:49:23 -07:00
parent 17a81ac89e
commit 66fdcfa3bd
2 changed files with 32 additions and 7 deletions

View file

@ -720,9 +720,21 @@ def build_turn_context(
tools=agent.tools or None,
)
_compressor = agent.context_compressor
agent._turn_preflight_display_snapshot = (
_compressor.snapshot_preflight_display_tokens()
# getattr guard: minimal compressor doubles (SimpleNamespace in the
# engine-preflight tests) and plugin context engines lack this
# ContextCompressor-only method — absence means no snapshot, and the
# finalizer's rollback stays disarmed for the turn (display-only).
_snapshot_fn = getattr(
_compressor, "snapshot_preflight_display_tokens", None
)
if callable(_snapshot_fn):
_snapshot_val = _snapshot_fn()
# Type pin: MagicMock compressors return truthy Mock objects —
# only a real int snapshot may arm the interrupted-turn rollback.
if isinstance(_snapshot_val, int) and not isinstance(
_snapshot_val, bool
):
agent._turn_preflight_display_snapshot = _snapshot_val
_defer_preflight = getattr(
_compressor,
"should_defer_preflight_to_real_usage",

View file

@ -205,18 +205,31 @@ def finalize_turn(
# request. Roll that estimate back only when an interrupt wins the race
# before any successful provider response. Compaction state remains owned
# by the real-usage/post-compaction path, including its ``-1`` sentinel.
# Guard rules (test-double density on this path is high):
# - snapshot is type-pinned to a real int — MagicMock agents auto-create
# truthy Mock attributes that must never arm the rollback;
# - the received-response flag is pinned to ``is not True`` — its real
# domain is True/False, and only a literal True means a provider
# response completed;
# - the compressor method gets a getattr+callable guard — SimpleNamespace
# compressor doubles and plugin context engines lack it.
_preflight_snapshot = getattr(
agent, "_turn_preflight_display_snapshot", None
)
if (
interrupted
and _preflight_snapshot is not None
and not getattr(agent, "_turn_received_provider_response", False)
interrupted is True
and isinstance(_preflight_snapshot, int)
and not isinstance(_preflight_snapshot, bool)
and getattr(agent, "_turn_received_provider_response", False) is not True
and getattr(agent, "context_compressor", None) is not None
):
agent.context_compressor.rollback_interrupted_preflight_display_tokens(
_preflight_snapshot
_rollback_fn = getattr(
agent.context_compressor,
"rollback_interrupted_preflight_display_tokens",
None,
)
if callable(_rollback_fn):
_rollback_fn(_preflight_snapshot)
# Post-loop cleanup must never lose the response. Trajectory save,
# resource teardown, and session persistence all touch fallible