Skip to content

fix(compression): disable compression on background-review fork (#38727) - #41708

Merged
teknium1 merged 1 commit into
mainfrom
fix/compression-review-fork-38727
Jun 8, 2026
Merged

fix(compression): disable compression on background-review fork (#38727)#41708
teknium1 merged 1 commit into
mainfrom
fix/compression-review-fork-38727

Conversation

@teknium1

@teknium1 teknium1 commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Summary

The background-review fork can no longer fork the parent session it shares a session_id with — closing the cross-turn gap left by the per-session compression lock (#38727).

Root cause: the per-session lock only serialises a same-window concurrent compression race. If review_agent won that race, its new child session was never adopted by the gateway (the fork is single-lifecycle — one run_conversation, then dies). The next foreground turn started from the stale parent and compressed it again, leaving the same parent with two sibling children.

Changes

  • agent/background_review.py: set review_agent.compression_enabled = False on the fork. Both compression trigger sites in conversation_loop.py (preflight + mid-loop) gate on compression_enabled before calling _compress_context, so a fork with the flag cleared never reaches the rotation path. Review needs full context anyway — compressing would degrade the memory/skill summary.
  • tests/agent/test_compression_concurrent_fork.py: regression test driving the real _run_review_in_thread worker and asserting the fork inherits the parent session_id but has compression_enabled == False.

The per-session lock is kept as defense-in-depth for any future shared-session path.

Validation

Without fix With fix
new regression test FAILS (fork compresses → orphan child) PASSES
full file (4 tests) 4 passed

Implements the fix proposed by @lgYanami in #38727.

Closes #38727

Infographic

compression-fork-fix

…event cross-turn stale-parent fork

The per-session compression lock prevents same-window concurrent forks but
not cross-turn ones: the background-review fork shares the parent's
session_id, so if it won a compression race its new child session was never
adopted by the gateway (the fork is single-lifecycle). The next foreground
turn then started from the stale parent and compressed it again, leaving the
same parent with two sibling children.

Set review_agent.compression_enabled = False so the fork never triggers
compression. Both trigger sites in conversation_loop.py gate on
compression_enabled before calling _compress_context, so the fork can never
rotate the shared parent. Review needs full context anyway — compressing
would degrade the memory/skill summary.

The per-session lock is kept as defense-in-depth for any future shared-session
path. Adds a regression test that fails without the flag and passes with it.

Closes #38727
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint labels Jun 8, 2026
@liuhao1024

Copy link
Copy Markdown
Contributor

Positive verification — clean security review

Reviewed the diff, test, and rationale for the background-review fork compression guard. The fix is correct and well-scoped:

  1. Root cause is clearly documented — the fork shares the parent's session_id; winning a compression race would rotate the parent into an orphan child that the gateway never adopts, then the foreground turn compresses the stale parent again, producing two sibling children.

  2. Fix is minimal and sufficient — setting review_agent.compression_enabled = False short-circuits both compression trigger sites in conversation_loop.py (preflight and mid-loop), both of which gate on this flag before calling _compress_context.

  3. Test pins the contract at the source_run_review_in_thread is called synchronously with AIAgent.run_conversation patched (no LLM call), the constructed agent is captured, and the assertion checks compression_enabled is False with a clear regression message.

  4. No side effects — the review fork still inherits session_id and session_start for logging correlation; only the compression flag is cleared.

No issues found.

@github-actions

github-actions Bot commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

🔎 Lint report: fix/compression-review-fork-38727 vs origin/main

ruff

Total: 0 on HEAD, 0 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 0 pre-existing issues carried over.

ty (type checker)

Total: 10032 on HEAD, 10031 on base (🆕 +1)

🆕 New issues (1):

Rule Count
unresolved-attribute 1
First entries
agent/background_review.py:462: [unresolved-attribute] unresolved-attribute: Unresolved attribute `compression_enabled` on type `AIAgent`

✅ Fixed issues: none

Unchanged: 5200 pre-existing issues carried over.

Diagnostics are surfaced as warnings — this check never fails the build.

@teknium1
teknium1 merged commit a77bc2c into main Jun 8, 2026
23 checks passed
@teknium1
teknium1 deleted the fix/compression-review-fork-38727 branch June 8, 2026 05:06
changman pushed a commit to changman/hermes-agent that referenced this pull request Jun 10, 2026
…event cross-turn stale-parent fork (NousResearch#41708)

The per-session compression lock prevents same-window concurrent forks but
not cross-turn ones: the background-review fork shares the parent's
session_id, so if it won a compression race its new child session was never
adopted by the gateway (the fork is single-lifecycle). The next foreground
turn then started from the stale parent and compressed it again, leaving the
same parent with two sibling children.

Set review_agent.compression_enabled = False so the fork never triggers
compression. Both trigger sites in conversation_loop.py gate on
compression_enabled before calling _compress_context, so the fork can never
rotate the shared parent. Review needs full context anyway — compressing
would degrade the memory/skill summary.

The per-session lock is kept as defense-in-depth for any future shared-session
path. Adds a regression test that fails without the flag and passes with it.

Closes NousResearch#38727
alt-glitch pushed a commit that referenced this pull request Jun 14, 2026
…event cross-turn stale-parent fork (#41708)

The per-session compression lock prevents same-window concurrent forks but
not cross-turn ones: the background-review fork shares the parent's
session_id, so if it won a compression race its new child session was never
adopted by the gateway (the fork is single-lifecycle). The next foreground
turn then started from the stale parent and compressed it again, leaving the
same parent with two sibling children.

Set review_agent.compression_enabled = False so the fork never triggers
compression. Both trigger sites in conversation_loop.py gate on
compression_enabled before calling _compress_context, so the fork can never
rotate the shared parent. Review needs full context anyway — compressing
would degrade the memory/skill summary.

The per-session lock is kept as defense-in-depth for any future shared-session
path. Adds a regression test that fails without the flag and passes with it.

Closes #38727
davidgut1982 pushed a commit to davidgut1982/hermes-agent that referenced this pull request Jun 17, 2026
…event cross-turn stale-parent fork (NousResearch#41708)

The per-session compression lock prevents same-window concurrent forks but
not cross-turn ones: the background-review fork shares the parent's
session_id, so if it won a compression race its new child session was never
adopted by the gateway (the fork is single-lifecycle). The next foreground
turn then started from the stale parent and compressed it again, leaving the
same parent with two sibling children.

Set review_agent.compression_enabled = False so the fork never triggers
compression. Both trigger sites in conversation_loop.py gate on
compression_enabled before calling _compress_context, so the fork can never
rotate the shared parent. Review needs full context anyway — compressing
would degrade the memory/skill summary.

The per-session lock is kept as defense-in-depth for any future shared-session
path. Adds a regression test that fails without the flag and passes with it.

Closes NousResearch#38727
T02200059 pushed a commit to T02200059/hermes-agent that referenced this pull request Jun 18, 2026
…event cross-turn stale-parent fork (NousResearch#41708)

The per-session compression lock prevents same-window concurrent forks but
not cross-turn ones: the background-review fork shares the parent's
session_id, so if it won a compression race its new child session was never
adopted by the gateway (the fork is single-lifecycle). The next foreground
turn then started from the stale parent and compressed it again, leaving the
same parent with two sibling children.

Set review_agent.compression_enabled = False so the fork never triggers
compression. Both trigger sites in conversation_loop.py gate on
compression_enabled before calling _compress_context, so the fork can never
rotate the shared parent. Review needs full context anyway — compressing
would degrade the memory/skill summary.

The per-session lock is kept as defense-in-depth for any future shared-session
path. Adds a regression test that fails without the flag and passes with it.

Closes NousResearch#38727
waefrebeorn pushed a commit to waefrebeorn/slermes that referenced this pull request Jul 2, 2026
…event cross-turn stale-parent fork (NousResearch#41708)

The per-session compression lock prevents same-window concurrent forks but
not cross-turn ones: the background-review fork shares the parent's
session_id, so if it won a compression race its new child session was never
adopted by the gateway (the fork is single-lifecycle). The next foreground
turn then started from the stale parent and compressed it again, leaving the
same parent with two sibling children.

Set review_agent.compression_enabled = False so the fork never triggers
compression. Both trigger sites in conversation_loop.py gate on
compression_enabled before calling _compress_context, so the fork can never
rotate the shared parent. Review needs full context anyway — compressing
would degrade the memory/skill summary.

The per-session lock is kept as defense-in-depth for any future shared-session
path. Adds a regression test that fails without the flag and passes with it.

Closes NousResearch#38727
santhreal pushed a commit to santhreal/hermes-agent that referenced this pull request Jul 13, 2026
…event cross-turn stale-parent fork (NousResearch#41708)

The per-session compression lock prevents same-window concurrent forks but
not cross-turn ones: the background-review fork shares the parent's
session_id, so if it won a compression race its new child session was never
adopted by the gateway (the fork is single-lifecycle). The next foreground
turn then started from the stale parent and compressed it again, leaving the
same parent with two sibling children.

Set review_agent.compression_enabled = False so the fork never triggers
compression. Both trigger sites in conversation_loop.py gate on
compression_enabled before calling _compress_context, so the fork can never
rotate the shared parent. Review needs full context anyway — compressing
would degrade the memory/skill summary.

The per-session lock is kept as defense-in-depth for any future shared-session
path. Adds a regression test that fails without the flag and passes with it.

Closes NousResearch#38727
donbowman pushed a commit to donbowman/hermes-agent that referenced this pull request Jul 13, 2026
…event cross-turn stale-parent fork (NousResearch#41708)

The per-session compression lock prevents same-window concurrent forks but
not cross-turn ones: the background-review fork shares the parent's
session_id, so if it won a compression race its new child session was never
adopted by the gateway (the fork is single-lifecycle). The next foreground
turn then started from the stale parent and compressed it again, leaving the
same parent with two sibling children.

Set review_agent.compression_enabled = False so the fork never triggers
compression. Both trigger sites in conversation_loop.py gate on
compression_enabled before calling _compress_context, so the fork can never
rotate the shared parent. Review needs full context anyway — compressing
would degrade the memory/skill summary.

The per-session lock is kept as defense-in-depth for any future shared-session
path. Adds a regression test that fails without the flag and passes with it.

Closes NousResearch#38727
Gravezzz pushed a commit to Gravezzz/hermes-agent that referenced this pull request Jul 21, 2026
…event cross-turn stale-parent fork (NousResearch#41708)

The per-session compression lock prevents same-window concurrent forks but
not cross-turn ones: the background-review fork shares the parent's
session_id, so if it won a compression race its new child session was never
adopted by the gateway (the fork is single-lifecycle). The next foreground
turn then started from the stale parent and compressed it again, leaving the
same parent with two sibling children.

Set review_agent.compression_enabled = False so the fork never triggers
compression. Both trigger sites in conversation_loop.py gate on
compression_enabled before calling _compress_context, so the fork can never
rotate the shared parent. Review needs full context anyway — compressing
would degrade the memory/skill summary.

The per-session lock is kept as defense-in-depth for any future shared-session
path. Adds a regression test that fails without the flag and passes with it.

Closes NousResearch#38727
leewenjie pushed a commit to leewenjie/hermes-agent that referenced this pull request Aug 7, 2026
…event cross-turn stale-parent fork (NousResearch#41708)

The per-session compression lock prevents same-window concurrent forks but
not cross-turn ones: the background-review fork shares the parent's
session_id, so if it won a compression race its new child session was never
adopted by the gateway (the fork is single-lifecycle). The next foreground
turn then started from the stale parent and compressed it again, leaving the
same parent with two sibling children.

Set review_agent.compression_enabled = False so the fork never triggers
compression. Both trigger sites in conversation_loop.py gate on
compression_enabled before calling _compress_context, so the fork can never
rotate the shared parent. Review needs full context anyway — compressing
would degrade the memory/skill summary.

The per-session lock is kept as defense-in-depth for any future shared-session
path. Adds a regression test that fails without the flag and passes with it.

Closes NousResearch#38727
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint P2 Medium — degraded but workaround exists type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: per-session compression lock only prevents same-window concurrent fork, but not cross-turn stale-parent fork

3 participants