Conversation
e2ef308 to
fb30dae
Compare
|
Rebased this PR onto current Local verification on head
CI is red only on I attempted to rerun the failed upstream job, but GitHub returned Could a maintainer please rerun the failed slice 5/12 job? |
|
This was generated by AI during triage. Summary: Problems:
Solution: Checked against |
|
Heads-up from the forensics on #82001: @Al3xand3r1987 reproduced a flush failure that this PR's resolver can't recover — a two-level lineage ( |
|
Thank you for establishing the closed-parent final-flush adoption direction here. I opened draft #86099 to cover the two blockers already identified in this thread: parent-bound handling of inherited foreign branch/delegate/tool markers, and the multi-generation A -> B -> C lineage reproduced in #82001. It also revalidates the selected tip inside the transcript write transaction and applies the same invariant to gateway/recovery writers.\n\n#86099 explicitly credits this PR and is not a request to merge both implementations; maintainers can choose the clean superseding path once its independent review/checks complete. |
|
Quick follow-up on the resolver choice — the multi-hop case from #82001 applies here too. The selection query in This isn't hypothetical: we now have four independent multi-hop reports in the wild — our own 2-hop repro, juliedarkheart's 2-hop WebUI-restart case, and 3-hop chains from gyu-bot and ayushnangia (all in #82001). The atomic selection+append is a solid TOCTOU close. If multi-hop turns out to be common, promoting selection to the transitive tip ( |
|
Thanks @KCAYAAI — this PR had the right instincts from the start, and its fail-closed framing (never guess a target session; preserve the batch for a safe retry; report a compression-lineage failure instead of "full disk") is exactly the contract the landed fix keeps. Heads-up that #86666 has now merged a full-chain version of this fix and closed #82001: after this PR was opened, the issue thread reproduced lineages with ≥2 compression hops ( That supersedes the resolver here, so this PR is no longer needed as-is — but closing it is your call, so I'll leave that to you. Thanks again for kicking off the fix and for framing the fail-closed contract correctly. |
fix(agent): adopt live compression child on flush The transaction-level reroute (resolve child + insert under one BEGIN IMMEDIATE via
No blocking issues. |
|
Thanks @teknium1 — appreciated. The fail-closed framing was the part we wanted reviewed hardest (never guess a target session, preserve the batch for retry), so this is good to hear. Keeping the depth-1 resolver note from @Al3xand3r1987 on the radar for the multi-hop case. |
Duplicate of #86409; the mechanism is the same. |
|
Noting the triage comment: #86409 implements the transitive tip resolution (we verified its suite — 11/11), while this PR's distinct contribution is the fail-closed selection policy: never guess a target session, preserve the batch for a safe retry, report a compression error instead. Complementary boundaries rather than the same mechanism. If maintainers judge the mechanism absorbed, the one thing worth preserving from here is that fail-closed framing — happy to fold it into #86409 as a follow-up if that's the cleaner landing. |
Summary
A transcript flush that loses a context-compression race now follows the session's unique live continuation instead of aborting the turn against the closed parent.
If Hermes cannot select exactly one live continuation, it still fails closed, preserves the unpersisted markers for a safe retry, and reports a compression-lineage failure rather than suggesting that the disk is full.
Problem
SessionDB.append_messages_batch()correctly rejects writes to sessions closed by compression.AIAgent._flush_messages_to_session_db_unlocked()treated that specific rejection like any other persistence failure, so an otherwise healthy turn could stop after another compression path rotated the session.The resulting
session_persistence_failedmessage pointed operators toward disk capacity orstate.dbpermissions even when both were healthy.Changes
CompressionSessionClosedErrorat the agent's atomic batch-flush boundary.BEGIN IMMEDIATEtransaction, preventing a second publisher from making the lineage ambiguous between selection and commit.compression_session_closedwith actionable, non-disk guidance.Relationship to existing work
This is distinct from #77386 / #77410 / #77799, which address writers waiting on a compression lock, and from #57895 / #79322, which route queued gateway follow-ups after a completed turn. This patch handles the lower-level agent transcript flush after the parent has already been closed and a continuation published.
Validation
pytest tests/run_agent/test_compression_closed_flush_reroute.py tests/run_agent/test_860_dedup.py tests/run_agent/test_tool_call_incremental_persistence.py tests/run_agent/test_turn_completion_explainer.py -q -o addopts=— 25 passedpytest tests/agent/test_compression_concurrent_fork.py tests/test_hermes_state_compression_busy_retry.py tests/hermes_state/test_append_messages_batch.py tests/hermes_state -q -o addopts=— 99 passedpytest tests/gateway/test_session.py tests/agent/test_turn_finalizer_final_response_persistence.py -q -o addopts=— 64 passedruff check hermes_state.py run_agent.py agent/conversation_loop.py agent/turn_finalizer.py tests/run_agent/test_compression_closed_flush_reroute.py tests/run_agent/test_tool_call_incremental_persistence.py tests/hermes_state/test_append_messages_batch.pygit diff --cached --checkChecklist
Code
Documentation & Housekeeping
Agent Disclosure
Created with Hermes Agent under direct human instruction and reviewed before submission.