fix: descendant CTE must dedup (UNION) to survive parent-chain cycles

The #39140 CTE used UNION ALL, which recurses forever if a corrupted
parent chain loops (a -> b -> a) — reproduced: query never returns. The
old Python walk was cycle-safe via a seen-set. UNION dedups the working
set and terminates. Regression test added and mutation-verified (UNION
ALL hangs the test, UNION passes).
This commit is contained in:
kshitijk4poor 2026-07-08 17:41:06 +05:30 committed by kshitij
parent 206c042392
commit 1e2ad17afd
2 changed files with 24 additions and 1 deletions

View file

@ -9306,7 +9306,7 @@ def _session_latest_descendant(session_id: str, db):
"""
WITH RECURSIVE descendants(id, parent_session_id, started_at) AS (
SELECT id, parent_session_id, started_at FROM sessions WHERE id = ?
UNION ALL
UNION
SELECT s.id, s.parent_session_id, s.started_at
FROM sessions s
JOIN descendants d ON s.parent_session_id = d.id

View file

@ -1138,6 +1138,29 @@ class TestWebServerEndpoints:
assert worker_resp.status_code == 200
assert worker_resp.json()["session_id"] == "worker-tip"
def test_latest_descendant_survives_parent_cycle(self):
"""Regression for the #39140 CTE salvage: a corrupted parent chain
that loops (a -> b -> a) must terminate (UNION dedup) instead of
recursing forever like UNION ALL would."""
from hermes_state import SessionDB
db = SessionDB()
try:
db.create_session(session_id="cyc-a", source="cli")
db.create_session(
session_id="cyc-b", source="cli", parent_session_id="cyc-a"
)
db._conn.execute(
"UPDATE sessions SET parent_session_id='cyc-b' WHERE id='cyc-a'"
)
db._conn.commit()
finally:
db.close()
resp = self.client.get("/api/sessions/cyc-a/latest-descendant")
assert resp.status_code == 200
assert resp.json()["session_id"] == "cyc-b"
def test_analytics_endpoints_read_requested_profile(self):
from hermes_state import SessionDB
from hermes_cli import profiles as profiles_mod