From 11330ff68ec89f7cdf1b7de49a4a3436ef3df58f Mon Sep 17 00:00:00 2001 From: Michael Kuehl Date: Wed, 22 Apr 2026 09:06:40 +0200 Subject: [PATCH] fix(engine): skip redundant cleanup ingestion when barrier already ran (fixes #505) --- agent_fox/engine/run.py | 20 ++++---- tests/unit/engine/test_double_ingestion.py | 59 ++++++++++++++++++++++ 2 files changed, 70 insertions(+), 9 deletions(-) create mode 100644 tests/unit/engine/test_double_ingestion.py diff --git a/agent_fox/engine/run.py b/agent_fox/engine/run.py index 21e681e0..0fd0f26f 100644 --- a/agent_fox/engine/run.py +++ b/agent_fox/engine/run.py @@ -288,21 +288,23 @@ def _barrier_sync(infra: dict[str, Any], config: Any) -> None: ) except Exception: logger.warning("Barrier ingestion failed", exc_info=True) + finally: + infra["_barrier_ingestion_ran"] = True def _cleanup_infrastructure(infra: dict[str, Any], config: Any) -> None: """Clean up infrastructure resources.""" knowledge_db = infra["knowledge_db"] - # Re-ingest to capture new commits/ADRs - try: - run_background_ingestion( - knowledge_db.connection, - config.knowledge, - Path.cwd(), - ) - except Exception: - logger.warning("Final ingestion failed", exc_info=True) + if not infra.get("_barrier_ingestion_ran", False): + try: + run_background_ingestion( + knowledge_db.connection, + config.knowledge, + Path.cwd(), + ) + except Exception: + logger.warning("Final ingestion failed", exc_info=True) # Close sinks and DB try: diff --git a/tests/unit/engine/test_double_ingestion.py b/tests/unit/engine/test_double_ingestion.py new file mode 100644 index 00000000..edc46f19 --- /dev/null +++ b/tests/unit/engine/test_double_ingestion.py @@ -0,0 +1,59 @@ +"""Regression tests for double git ingestion at end-of-run (issue #505). + +Verifies that _cleanup_infrastructure skips run_background_ingestion when +a sync barrier already ran ingestion during the same engine run. +""" + +from __future__ import annotations + +from unittest.mock import MagicMock, patch + +import pytest + + +@pytest.fixture +def _mock_ingestion(): + """Patch run_background_ingestion and return the mock.""" + with patch("agent_fox.engine.run.run_background_ingestion") as mock: + yield mock + + +class TestCleanupSkipsIngestionAfterBarrier: + """_cleanup_infrastructure must not re-ingest when a barrier already did.""" + + def test_cleanup_skips_ingestion_after_barrier(self, _mock_ingestion: MagicMock) -> None: + from agent_fox.engine.run import _barrier_sync, _cleanup_infrastructure + + config = MagicMock() + infra = {"knowledge_db": MagicMock(), "sink_dispatcher": MagicMock()} + + _barrier_sync(infra, config) + assert _mock_ingestion.call_count == 1 + + _mock_ingestion.reset_mock() + _cleanup_infrastructure(infra, config) + _mock_ingestion.assert_not_called() + + def test_cleanup_ingests_when_no_barrier_ran(self, _mock_ingestion: MagicMock) -> None: + from agent_fox.engine.run import _cleanup_infrastructure + + config = MagicMock() + infra = {"knowledge_db": MagicMock(), "sink_dispatcher": MagicMock()} + + _cleanup_infrastructure(infra, config) + _mock_ingestion.assert_called_once() + + def test_barrier_failure_still_sets_flag(self, _mock_ingestion: MagicMock) -> None: + from agent_fox.engine.run import _barrier_sync, _cleanup_infrastructure + + _mock_ingestion.side_effect = RuntimeError("ingestion failed") + + config = MagicMock() + infra = {"knowledge_db": MagicMock(), "sink_dispatcher": MagicMock()} + + _barrier_sync(infra, config) + + _mock_ingestion.reset_mock() + _mock_ingestion.side_effect = None + _cleanup_infrastructure(infra, config) + _mock_ingestion.assert_not_called()