mirror of
https://github.com/NousResearch/hermes-agent.git
synced 2026-07-31 19:16:29 +00:00
Systematic prune per AGENTS.md test policy, one pass over every major test tree (gateway, hermes_cli, tools, agent, run_agent, plugins, cli, cron, tui_gateway, honcho/openviking, root-level): - DELETE: source-reading tests (read_text/getsource on prod files), change-detector tests (exact catalog counts, model-name snapshots, config version literals), mock-echo tests (assert a mock returns what it was told), assertion-free/trivial tests, near-duplicate parametrizations (boundaries + one representative kept), async/sync twin duplicates, cosmetic within-file variations. - KEEP (mandatory): security/redaction/approval guards, message-role alternation invariants, prompt-caching/deterministic-call-id invariants, issue-number regression tests (deduped), E2E tests. - 6 test files deleted outright (script-style/no-assert or fully redundant); conftest.py, fakes/, fixtures/ untouched. - tests/acp/conftest.py added: autouse fixture stubs the live models.dev/GitHub/Copilot/Anthropic inventory fetches that ACP server tests performed on every session create — test_server.py 147s → 3.4s, and the tests are now genuinely hermetic. - Sleep-based slowness shrunk where safe (codex_ttfb_watchdog, compression_concurrent_fork, etc.); no wall-clock assertion tightened. Verification: full hermetic suite via scripts/run_tests.sh — 2439 files, 31,130 tests passed, 0 failed, 0 flaky retries, 315s wall (baseline: 583s wall, 13,564s subprocess CPU).
139 lines
5.2 KiB
Python
139 lines
5.2 KiB
Python
"""Regression tests for #57682 — holographic auto_extract harvested
|
|
context-compaction handoff summaries into fact_store, and ran even when
|
|
configured off.
|
|
|
|
Two compounding defects:
|
|
|
|
1. Gate: the plugin's config schema declares ``auto_extract`` as a string enum
|
|
(``"false"``/``"true"``), and the ``on_session_end`` gate used plain
|
|
truthiness — ``not "false"`` is ``False`` — so extraction ran despite being
|
|
explicitly configured off.
|
|
|
|
2. Eligibility: ``_auto_extract_facts`` scanned every ``role == "user"``
|
|
message. Compaction handoff summaries can be inserted as ``role="user"``
|
|
messages, and their prose reliably matches the decision patterns
|
|
(``we decided/agreed/chose``, ``the project uses/needs/requires``), so the
|
|
compactor's own output was stored as a durable ``project`` fact on every
|
|
session rollover that followed a compaction.
|
|
"""
|
|
|
|
import pytest
|
|
|
|
from agent.context_compressor import (
|
|
COMPRESSED_SUMMARY_METADATA_KEY,
|
|
SUMMARY_PREFIX,
|
|
_MERGED_PRIOR_CONTEXT_HEADER,
|
|
_MERGED_SUMMARY_DELIMITER,
|
|
is_compaction_summary_message,
|
|
)
|
|
from plugins.memory.holographic import HolographicMemoryProvider
|
|
|
|
|
|
def _make_provider(tmp_path, **config):
|
|
base = {"db_path": str(tmp_path / "memory_store.db"), "hrr_dim": 64}
|
|
base.update(config)
|
|
provider = HolographicMemoryProvider(config=base)
|
|
provider.initialize(session_id="test-session")
|
|
return provider
|
|
|
|
|
|
def _fact_contents(provider):
|
|
return [f["content"] for f in provider._store.list_facts(limit=100)]
|
|
|
|
|
|
def _user(content, **extra):
|
|
msg = {"role": "user", "content": content}
|
|
msg.update(extra)
|
|
return msg
|
|
|
|
|
|
DECISION_MSG = "we decided to use PostgreSQL for the persistence layer"
|
|
SUMMARY_MSG = (
|
|
f"{SUMMARY_PREFIX}\n## Historical Task Snapshot\n"
|
|
"The project uses a kanban board for all dispatch. "
|
|
"We agreed to route reviews through the fan-in consumer."
|
|
)
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Defect 1 — string-boolean gate
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
@pytest.mark.parametrize("off_value", [False, "false", "False", "no", "off", "0", None, ""])
|
|
def test_auto_extract_off_values_disable_extraction(tmp_path, off_value):
|
|
provider = _make_provider(tmp_path, auto_extract=off_value)
|
|
provider.on_session_end([_user(DECISION_MSG)])
|
|
assert _fact_contents(provider) == []
|
|
provider.shutdown()
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Defect 2 — compaction summaries harvested as facts
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def test_compaction_summary_not_harvested(tmp_path):
|
|
"""The exact failure mode from #57682: summary prose matches the decision
|
|
patterns but must not become a fact."""
|
|
provider = _make_provider(tmp_path, auto_extract=True)
|
|
provider.on_session_end([_user(SUMMARY_MSG)])
|
|
assert _fact_contents(provider) == []
|
|
provider.shutdown()
|
|
|
|
|
|
def test_metadata_marked_summary_not_harvested(tmp_path):
|
|
"""In-process summaries carry COMPRESSED_SUMMARY_METADATA_KEY even if a
|
|
future prefix rewrite changes the content sentinel."""
|
|
provider = _make_provider(tmp_path, auto_extract=True)
|
|
marked = _user(DECISION_MSG, **{COMPRESSED_SUMMARY_METADATA_KEY: True})
|
|
provider.on_session_end([marked])
|
|
assert _fact_contents(provider) == []
|
|
provider.shutdown()
|
|
|
|
|
|
def test_merged_into_tail_summary_suffix_not_harvested_prefix_content_ignored(tmp_path):
|
|
"""Merge-into-tail summaries embed the handoff prefix after the delimiter,
|
|
not at the start of the message. The wrapped pre-delimiter segment here has
|
|
no fact-pattern match, so nothing is harvested from either side."""
|
|
provider = _make_provider(tmp_path, auto_extract=True)
|
|
merged = _user(
|
|
f"{_MERGED_PRIOR_CONTEXT_HEADER}\nplease fix the login bug\n"
|
|
f"{_MERGED_SUMMARY_DELIMITER}\n{SUMMARY_MSG}"
|
|
)
|
|
provider.on_session_end([merged])
|
|
assert _fact_contents(provider) == []
|
|
provider.shutdown()
|
|
|
|
|
|
def test_real_user_messages_still_extracted_alongside_summary(tmp_path):
|
|
"""The guard must skip only the summary, not suppress extraction for the
|
|
genuine user turns around it."""
|
|
provider = _make_provider(tmp_path, auto_extract=True)
|
|
provider.on_session_end(
|
|
[
|
|
_user(SUMMARY_MSG),
|
|
_user("I prefer tabs over spaces for indentation"),
|
|
]
|
|
)
|
|
facts = _fact_contents(provider)
|
|
assert len(facts) == 1
|
|
assert "tabs over spaces" in facts[0]
|
|
provider.shutdown()
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# is_compaction_summary_message — public helper contract
|
|
# ---------------------------------------------------------------------------
|
|
|
|
|
|
def test_helper_detects_prefix_metadata_and_merged_forms():
|
|
assert is_compaction_summary_message(_user(SUMMARY_MSG))
|
|
assert is_compaction_summary_message(
|
|
_user("anything", **{COMPRESSED_SUMMARY_METADATA_KEY: True})
|
|
)
|
|
assert is_compaction_summary_message(
|
|
_user(f"prior tail\n{_MERGED_SUMMARY_DELIMITER}\n{SUMMARY_MSG}")
|
|
)
|
|
assert not is_compaction_summary_message(_user(DECISION_MSG))
|
|
assert not is_compaction_summary_message(_user(""))
|