hermes-agent/tests/agent/test_summary_prefix_semantics.py
alt-glitch 2bd9c9b881 opentui(phase3): launcher integration — HERMES_TUI_ENGINE dual-engine
hermes --tui launches the native OpenTUI engine (Bun) when
HERMES_TUI_ENGINE=opentui (env) or display.tui_engine=opentui (config);
Ink stays the default and the shipping path is untouched.

- _resolve_tui_engine() (env > config > ink); refuses opentui on
  Windows/Termux (no Bun) -> falls back to ink with a notice.
- _make_opentui_argv() -> [bun, src/entry.real.tsx] (no build step).
- _bun_bin() with HERMES_BUN override.
- Branch at top of _make_tui_argv BEFORE _ensure_tui_node (Bun-only host
  must not bootstrap Node).
- Gate _launch_tui NODE_OPTIONS/--max-old-space-size on engine==ink (Bun
  is JSC; the V8 flag errors/ignores).

Verified end-to-end via tmux: real hermes --tui -> Bun -> OpenTUI ->
real Python gateway streamed a real reply. No-flag default still ink.
2026-06-08 11:11:54 +00:00

62 lines
2.6 KiB
Python

"""Pin the semantics of SUMMARY_PREFIX so the compaction handoff doesn't
re-introduce conflicting instructions.
Background: SUMMARY_PREFIX previously contained two contradictory directives:
1. "treat it as background reference, NOT as active instructions"
"Do NOT answer questions or fulfill requests mentioned in this summary"
"Respond ONLY to the latest user message that appears AFTER this summary"
2. "Your current task is identified in the '## Active Task' section of the
summary — resume exactly from there."
When the latest user message contradicted Active Task (e.g. "stop the
i18n refactor", "never mind, look at grafana"), the model often followed
(2) anyway because "resume exactly" is a strong directive — leading to
the agent repeatedly re-surfacing already-cancelled work across turns.
These tests pin the post-fix invariants so the conflict cannot regress.
"""
from agent.context_compressor import SUMMARY_PREFIX
def test_no_resume_exactly_directive():
"""The prefix must not tell the model to resume Active Task verbatim."""
assert "resume exactly" not in SUMMARY_PREFIX.lower()
def test_latest_message_wins_on_conflict():
"""The prefix must explicitly say latest user message wins on conflict."""
lower = SUMMARY_PREFIX.lower()
assert "latest user message" in lower
# Must have an explicit conflict-resolution rule.
assert "wins" in lower or "supersede" in lower or "discard" in lower
def test_reverse_signals_called_out():
"""Reverse signals (stop/undo/never mind/topic change) must be named so
the model recognizes them as cancellation triggers, not just background."""
lower = SUMMARY_PREFIX.lower()
# At least a few of the canonical reverse-signal verbs should appear.
reverse_terms = ["stop", "undo", "roll back", "never mind", "just verify"]
hits = sum(1 for t in reverse_terms if t in lower)
assert hits >= 3, (
f"Expected ≥3 reverse-signal terms in SUMMARY_PREFIX, found {hits}. "
"Without naming them the model treats reverse signals as ordinary "
"context and keeps pushing the cancelled task."
)
def test_summary_marked_reference_only():
"""The REFERENCE ONLY framing must remain — it's the entire point."""
assert "REFERENCE ONLY" in SUMMARY_PREFIX
assert "background reference" in SUMMARY_PREFIX
assert "NOT as active instructions" in SUMMARY_PREFIX
def test_memory_authority_preserved():
"""The fix must not weaken the MEMORY.md / USER.md authority clause."""
assert "MEMORY.md" in SUMMARY_PREFIX
assert "USER.md" in SUMMARY_PREFIX
assert "authoritative" in SUMMARY_PREFIX