diff --git a/tests/integration/test_result_delivery_contract.py b/tests/integration/test_result_delivery_contract.py
new file mode 100644
index 0000000..13e474f
--- /dev/null
+++ b/tests/integration/test_result_delivery_contract.py
@@ -0,0 +1,125 @@
+"""Integration: the librarian -> bot RESULT delivery contract.
+
+A search that 'vanishes' (watchdog fires "zeżarło") means the result never
+reached the bot's inbound queue. These tests pin down the contract so we can
+tell a CODE break (wrong shape / uuid / auth handling) from a TRANSPORT break
+(the librarian can't reach the bot at all - wrong address/port). They prove the
+bot side is correct end to end, which isolates a systematic vanish to transport.
+
+The result the librarian sends is exactly:
+ {uuid: {DOI: {"Title": [
...], "type": }}}
+(see conjurer_librarian.answer_query -> final_result, POSTed to /conjurer).
+"""
+import threading
+import time
+
+import pytest
+
+import communication_subroutine as cs
+
+
+def _drain(queue):
+ while not queue.empty():
+ queue.get()
+
+
+@pytest.fixture
+def comm_threads():
+ cs.awaiting_q.clear()
+ _drain(cs.incoming_q)
+ _drain(cs.OUT_COMM_Q)
+ _drain(cs.IN_COMM_Q)
+ cs.API_KEY = None
+ stop = threading.Event()
+ workers = [
+ threading.Thread(target=cs.scan_queue, kwargs={"stop_event": stop}, daemon=True),
+ threading.Thread(target=cs.scan_incoming, kwargs={"stop_event": stop}, daemon=True),
+ ]
+ for worker in workers:
+ worker.start()
+ yield
+ stop.set()
+ for worker in workers:
+ worker.join(timeout=3)
+
+
+def _dispatch(uuid, query="kwas foliowy"):
+ """Mimic the bot dispatching a search: a QueryControl enters the comm queue
+ and scan_queue moves it into awaiting_q."""
+ qc = cs.QueryControl("siara", uuid, query, None)
+ cs.OUT_COMM_Q.put(qc)
+ deadline = time.time() + 2
+ while time.time() < deadline:
+ if any(getattr(r, "uuid", None) == uuid for r in list(cs.awaiting_q)):
+ return qc
+ time.sleep(0.01)
+ raise AssertionError("scan_queue never moved the query into awaiting_q")
+
+
+# The exact result the librarian emits for one found DOI.
+def _result_payload(uuid):
+ return {uuid: {"10.1000/xyz": {"Title": ["A Real Paper"], "type": "journal-article"}}}
+
+
+def test_librarian_result_reaches_bot_when_transport_is_fine(comm_threads):
+ _dispatch("uuid-ok")
+ client = cs.app.test_client()
+
+ resp = client.post("/conjurer", json=_result_payload("uuid-ok"))
+
+ assert resp.status_code == 200
+ got = cs.IN_COMM_Q.get(timeout=3)
+ assert got.uuid == "uuid-ok"
+ assert got.stop is True
+ # Exactly the shape check_data_q renders: entries[DOI]["Title"][0] / ["type"].
+ assert got.entries == {"10.1000/xyz": {"Title": ["A Real Paper"], "type": "journal-article"}}
+
+
+def test_empty_result_is_still_delivered_not_vanished(comm_threads):
+ # A search that found nothing sends {uuid: {}} - it must STILL be delivered
+ # (renders "niestety nie ma nic"), never look like a lost result.
+ _dispatch("uuid-empty")
+ client = cs.app.test_client()
+
+ resp = client.post("/conjurer", json={"uuid-empty": {}})
+
+ assert resp.status_code == 200
+ got = cs.IN_COMM_Q.get(timeout=3)
+ assert got.uuid == "uuid-empty"
+ assert got.entries == {}
+
+
+def test_wrong_api_key_rejects_result_so_it_vanishes(comm_threads):
+ # (b) reproduction: if the librarian's CONJURER_API_KEY differs from the
+ # bot's, /conjurer returns 401 and the result is never queued - the search
+ # silently vanishes exactly as reported.
+ cs.API_KEY = "bot-secret"
+ _dispatch("uuid-auth")
+ client = cs.app.test_client()
+
+ resp = client.post(
+ "/conjurer",
+ json=_result_payload("uuid-auth"),
+ headers={"X-Conjurer-Api-Key": "librarian-DIFFERENT-key"},
+ )
+
+ assert resp.status_code == 401
+ time.sleep(0.4)
+ assert cs.IN_COMM_Q.empty() # nothing delivered
+
+
+def test_uuid_mismatch_orphans_result_away_from_the_querent(comm_threads):
+ # (b) reproduction: if the uuid the librarian echoes back doesn't byte-match
+ # what the bot stored, scan_incoming can't match it -> it goes to the orphan
+ # path (posted to the fallback channel, NOT the querent) and the querent's
+ # pending entry is never cleared, so the watchdog still flags it lost.
+ _dispatch("uuid-stored")
+ client = cs.app.test_client()
+
+ client.post("/conjurer", json=_result_payload("uuid-DIFFERENT"))
+
+ got = cs.IN_COMM_Q.get(timeout=3)
+ assert got.author == "Orphaned"
+ assert got.uuid == "uuid-DIFFERENT"
+ # The original querent's record is untouched (still awaiting) - it "vanished".
+ assert any(getattr(r, "uuid", None) == "uuid-stored" for r in list(cs.awaiting_q))