fix(state): bind record_gateway_session_peer's ancestor walk to the queried parent - #80982
Conversation
|
This was generated by AI during triage. Summary: Problems:
Solution: Checked against |
record_gateway_session_peer()'s recursive ancestor walk was already fixed to bind _branched_from/_delegate_from marker checks to the specific parent being evaluated, since a compression continuation inherits the rotated agent's model_config verbatim and can carry a marker pointing at an unrelated ancestor. get_compression_tip() and resolve_resume_session_id() shared the same recursive-lineage shape but still used marker-presence-only (IS NULL) checks, so a continuation with a foreign marker was misclassified as a non-continuation child — get_compression_tip stopped one hop early and returned the dead parent, and resolve_resume_session_id's message-bearing walk skipped the continuation entirely, so --resume on the parent reloaded the pre-compression transcript missing every post-compression turn. Both functions now reuse the shared _NON_CONTINUATION_CHILD_FILTER_SQL filter (already used by find_live_compression_child), binding the marker check to the specific parent id being walked at each hop instead of checking marker absence.
9a7f0b5 to
9a547de
Compare
|
Rebased onto current `upstream/main` and extended the fix to the two other functions that share the same recursive-lineage shape as `record_gateway_session_peer()`:
Both now reuse the shared `_NON_CONTINUATION_CHILD_FILTER_SQL` filter (already used by `find_live_compression_child`), binding the marker check to the specific parent id at each hop instead of checking marker absence — same pattern as the original fix to `record_gateway_session_peer()`. Added two regression tests (`test_get_compression_tip_walks_past_continuation_with_foreign_marker`, `test_resolve_resume_session_id_walks_past_continuation_with_foreign_marker`) mirroring the existing foreign-marker tests in this file. Mutation-verified: both fail against pre-fix code. Full `tests/state/` suite (19/19) plus 560 tests across the broader session/resume/compression neighborhood pass. Ruff clean. Fresh competitor search: no open PR touches `get_compression_tip`/`resolve_resume_session_id`. #71486 rewrites `find_live_compression_child` only (already correctly fixed upstream); #64620 touches the same bug class but in `hermes_cli/web_server.py`'s separate dashboard query, not `hermes_state.py`. Squashed to a single commit on top of current `upstream/main`. |
Summary
Today's
a0801b878fixed a marker-PRESENCE-vs-marker-BINDING bug infind_live_compression_child/reopen_orphaned_compression_session: a real compression continuation can inherit the rotated agent'smodel_configverbatim (publish_compression_childcallers passagent._session_init_model_config), so a delegate subagent's continuation carries_delegate_from=<the delegate's own original parent>— a marker that exists but does not point at the parent being evaluated. Matching on presence alone misclassified such a continuation as a delegate/branch child.record_gateway_session_peer(include_compression_ancestors=True)'s recursive CTE (used bygateway/session.py::switch_sessionon an explicit/resumeto keep a compression lineage on one routing peer) has the exact same marker-presence check and was not touched by that fix — it wasn't in the list of read-only sitesa0801b878deliberately left alone (those fail closed and self-heal at next turn start; this is a write path with no such self-heal).Effect: resuming into a live continuation whose compression parent carries a foreign delegate/branch marker stops the lineage walk one hop too early — the parent keeps its old
session_keywhile the continuation gets the new one, splitting the lineage's routing peer across two keys.Fix
Same pattern as
a0801b878(COALESCE(json_extract(...), '') != <id>instead ofIS NULL), but bound to the recursive CTE's correlatedparent.idcolumn rather than a single top-level bound parameter — this walk evaluates a different parent at every hop (unlike the two functionsa0801b878fixed, which query one fixed parent/session id), so the exclusion has to be re-evaluated per row rather than bound once.Testing
test_record_gateway_session_peer_walks_multi_hop_compression_lineage(control: plain two-hop chain, no foreign markers) andtest_record_gateway_session_peer_walks_past_continuation_with_foreign_marker(repro) totests/state/test_compression_lineage_guard.py.hermes_state.pychange and confirmed the foreign-marker test fails on pre-fix code (AssertionError: assert None == 'new-routing-key').tests/state/,tests/gateway/test_session.py, and the fulltests/test_hermes_state.py(187 tests) all pass.ruff checkclean on both changed files.Competing PRs
Searched
record_gateway_session_peer,compression lineage marker,gateway session peer compression ancestors,switch_session compression ancestors— no PR targets this function. #71486 ("recover live tip across rotation chains") toucheshermes_state.pyand the same test file but a different function (find_latest_gateway_session_for_peer, ~250 lines away) with no overlap.