fix(dispatcher): restore dispatch.decision logging in CI#312
Merged
Conversation
When tests/memory runs first, Cognee calls structlog.configure( cache_logger_on_first_use=True), which causes the dispatcher's module-level log proxy to cache its .bind() to Cognee's processor list on first use. A later structlog.configure(processors=[_capture]) in this test does NOT retroactively rebind cached proxies, so dispatch.decision events fire through the old processor chain and the test's captured list stays empty. Drop the cached bind before exercising the dispatcher and restore it after. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
/diagnose root cause
tests/dispatcher/test_router.py::test_decision_logging_runs_on_every_resolutionfailed only inside the full pytest suite (the target test passes in isolation, and so does the wholetests/dispatcher/subtree). Test pollution. Captured-log output proved the production code DID emitdispatch.decision— but the test's_capturestructlog processor never received it:Bisection narrowed the polluting set to
tests/memory/*+tests/omni_router/test_api_wiring.py. Cognee (imported transitively by the memory tests) callsstructlog.configure(cache_logger_on_first_use=True)(seecognee/shared/logging_utils.py:435). With that flag set globally, the dispatcher's module-levellog = structlog.get_logger("hal0-dispatch")proxy caches its.bindto Cognee's processor list on first use. A subsequentstructlog.configure(processors=[_capture])in the test does NOT retroactively rebind cachedBoundLoggerLazyProxyinstances, solog.info("dispatch.decision", …)keeps firing through Cognee's processors and_capturenever sees the event.Fix is in the test: pop the cached
bindoff the dispatcher'slogproxy before exercising it (so the next.info()materializes a freshBoundLoggerbound to the_captureprocessor), then restore the cached bind in thefinallyblock so subsequent tests see the same proxy state. Production code is unchanged — the dispatcher's logging is correct.Offending commit
69418d7 feat(memory): ADR-0014 [memory.graph] schema + cognify gate + /api/memory routes (#257) (#287)— 2026-05-23. Introducedtests/memory/test_cognee_wrapper.py+tests/memory/test_graph_gate.py, which were the first tests in the suite to exercise Cognee initialization and therefore the first to setcache_logger_on_first_use=Trueglobally.The string
dispatch.decisionitself is unchanged sincedc8891a feat: Phase 1 port(history verified viagit log -S 'dispatch.decision'). No production regression.Verification commands
From
/tmp/hal0-dispatcher-fixon a fresh Python 3.11 venv (uv venv --python 3.11,uv pip install -e '.[dev]'):python -m pytest tests/dispatcher/test_router.py -xvs— 13 passedpython -m pytest tests/dispatcher/— 81 passedpython -m pytest tests/dispatcher/ tests/api/ -x— 408 passed, 3 skipped (broader smoke per brief)python -m pytest tests/memory tests/omni_router/test_api_wiring.py tests/dispatcher/test_router.py::test_decision_logging_runs_on_every_resolution— 25 passed (exact polluting combo that previously failed)python -m pytest— 1649 passed, 3 skipped (full suite; was 1648 passed + 1 failed before the fix)ruff check src/hal0/dispatcher/ tests/dispatcher/— All checks passedruff format --check src/hal0/dispatcher/ tests/dispatcher/— 15 files already formattedDiff
One file, 16 insertions, 1 deletion —
tests/dispatcher/test_router.pyonly. No production code changed.Coordination
Four sibling PRs were blocked on this failure (#306, #307, #308, #309). They should re-run CI once this lands; their changes don't touch the dispatcher or memory areas so a green base should clear their
python (3.11)runs.