Skip to content

fix(state): stop delegate/tool children corrupting compression lineage - #79193

Merged
kshitijk4poor merged 1 commit into
NousResearch:mainfrom
kshitijk4poor:fix/79024-lineage-fork-children
Aug 5, 2026
Merged

fix(state): stop delegate/tool children corrupting compression lineage#79193
kshitijk4poor merged 1 commit into
NousResearch:mainfrom
kshitijk4poor:fix/79024-lineage-fork-children

Conversation

@kshitijk4poor

Copy link
Copy Markdown
Collaborator

Summary

Delegate and tool child sessions no longer corrupt a conversation's compression lineage — session .md export now follows the real continuation instead of a subagent's transcript.

Root cause: get_compression_lineage's forward walk accepted any non-branch child of a compressed session as the compression continuation. A delegate subagent (_delegate_from) or tool-tagged (source="tool") child created before the real continuation row — the common runtime ordering, since the subagent exists before compression rotates the session — was picked as the lineage successor.

Context (real-world impact)

Reproduced on current main: a CLI session that spawns a delegate child and then compresses exports the delegate's private transcript as its continuation:

main:  lineage(root) = ['root', 'delegate']          # subagent hijacks the chain
       lineage(continuation) = ['continuation']      # real continuation orphaned
       export_session_lineage('root') messages: ['root user msg', 'DELEGATE PRIVATE msg']

fixed: lineage(root) = ['root', 'continuation']
       export messages: ['root user msg', 'continuation msg']

Anyone using /export (or anything built on export_session_lineage / get_compression_lineage) on a session that delegated work before compressing gets the wrong transcript today.

Changes

  • hermes_state.py: rename _is_branch_child_row_is_explicit_fork_child_row; treat _delegate_from and source="tool" rows as explicit forks alongside _branched_from; the forward walk now requires _is_compression_child_row instead of merely excluding branches.
  • tests/hermes_state/test_session_md_export.py: extend the lineage test with delegate/tool isolation asserts; new regression test for the fork-child-before-continuation ordering including export content (mutation-checked — fails on main, passes with the fix).

Validation

Result
tests/hermes_state/ 49 passed
tests/run_agent/ 1438 passed, 1 pre-existing failure (reproduces on clean upstream/main)
Mutation check new test fails on main's hermes_state.py, passes with fix
ruff clean

Sliced from #79024 by @RyderFreeman4Logos — cherry-picked with authorship preserved. The prompt-cache-scope portion of that PR is a design question tracked in #79017 (alongside #78956) and is intentionally not included here.

@kshitijk4poor
kshitijk4poor enabled auto-merge (rebase) August 5, 2026 08:13
@kshitijk4poor
kshitijk4poor disabled auto-merge August 5, 2026 08:14
get_compression_lineage's forward walk accepted any non-branch child as
the compression continuation. Delegate subagent rows (_delegate_from)
and tool-tagged rows (source=tool) created before the real continuation
were picked as the lineage successor, so the lineage — and session .md
export built on it — followed a subagent's transcript instead of the
actual conversation continuation.

Rename _is_branch_child_row to _is_explicit_fork_child_row, treat
_delegate_from and source=tool rows as explicit forks alongside
_branched_from, and require _is_compression_child_row in the forward
walk instead of merely excluding branches.

Sliced from PR NousResearch#79024 by @RyderFreeman4Logos (the cache-scope portion
of that PR is tracked separately in NousResearch#79017).
@kshitijk4poor
kshitijk4poor force-pushed the fix/79024-lineage-fork-children branch from 829cf6a to 86ff5f6 Compare August 5, 2026 08:16
@kshitijk4poor
kshitijk4poor enabled auto-merge (rebase) August 5, 2026 08:16
@kshitijk4poor
kshitijk4poor merged commit 84e93ff into NousResearch:main Aug 5, 2026
35 checks passed
kshitijk4poor added a commit that referenced this pull request Aug 15, 2026
…ation

Legacy compaction mode (compression.in_place: false) rotates the physical
session_id mid-conversation. The prompt-cache scope introduced in #79161 was
derived from that physical id, so every rotation moved the same conversation
into a fresh cache bucket - the prompt cache went cold at every rotation
boundary (#79017).

Fix: resolve a rotation-stable logical scope - the compression-lineage ROOT
of the current session (SessionDB.get_compression_lineage, fork-aware
post-#79193) - once per turn, memoized per transcript segment, and prefer it
over the physical session_id at every prompt_cache_key derivation site:

- agent/prompt_cache_scope.py (new): resolve_prompt_cache_scope(agent) -
  lineage-root walk with per-segment memo; falls back to the physical id
  when no DB is attached or the walk fails, degrading to pre-fix behavior.
- transports/codex.py: build_kwargs accepts cache_scope_id and prefers it
  for the body prompt_cache_key, the xAI x-grok-conv-id header, and the
  Codex x-client-request-id routing header. The Codex session_id header
  keeps the raw physical id (transcript identity, #57012 contract).
- transports/chat_completions.py: _add_prompt_cache_key accepts
  cache_scope_id with the same precedence.
- chat_completion_helpers.py: build_api_kwargs threads the resolved scope
  into all three build_kwargs call sites (codex, profile, legacy).
- auxiliary_client.py: set_runtime_main carries cache_scope; the aux
  Responses cache-key site prefers it over the physical session_id.
- turn_context.py: resolves the scope once per turn and threads it through
  set_runtime_main (no DB walk on the per-API-call hot path).

Scope semantics preserved from #79161: /new starts a fresh scope (new
lineage), /branch children, delegate subagents, and tool children stay
isolated (explicit-fork exclusion in get_compression_lineage), unrelated
sessions keep distinct buckets, and cron per-fire timestamps still
normalize via _cache_scope_from_session_id.

Default installs compact in place (session_id never rotates), so they hit
the memo and produce byte-identical keys to before.

Fixes #79017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants