fix: heartbeat is_current_turn false after steer via command() - #5821
fix: heartbeat is_current_turn false after steer via command()#5821Christian-Sidak wants to merge 1 commit into
Conversation
When a steer happens, `_start_turn()` overwrites the durable row's `turn_id` with the new turn before the old turn's next heartbeat fires. The original code compared the durable row's `turn_id` to decide if a lock loss was a cancel vs. a first-beat race. For the steer case, the row already shows the new turn_id, so `turn_was_established` is False and `is_current_turn` stays True -- the old turn thinks it is still current even though it has been displaced. Fix: when `refresh_alive` fails, read the alive lock's current holder directly. If the lock is held by a different turn_id, the old turn was displaced by a steer and `is_current_turn` is set to False. The durable row check (`turn_was_established`) is kept as the fallback for the cancel case (lock absent, row matches). Adds a regression test that drives the real steer write path via `command()` with `force=True` rather than manually simulating the lock cancellation, matching the exact sequence from issue Agenta-AI#5790. Fixes Agenta-AI#5790 Signed-off-by: Christian-Sidak <61099993+Christian-Sidak@users.noreply.github.com>
|
@Christian-Sidak is attempting to deploy a commit to the agenta projects Team on Vercel. A member of the Team first needs to authorize it. |
|
Hi @Christian-Sidak, thanks for opening a pull request. 🙏 This PR was automatically closed because it does not yet meet our contribution requirements:
We ask for this so every change is documented and demonstrably tested before review. How to get it reopened See the Contributing guide and Creating your first PR. If you think this was closed in error, leave a comment and a maintainer will take a look. |
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
Disabled knowledge base sources:
📝 WalkthroughSummary by CodeRabbit
WalkthroughHeartbeat interruption detection now checks Redis alive-lock ownership. Displaced turns become non-current when another turn owns the lock. Established turns become non-current when the lock disappears. A command-path regression test covers force steering. ChangesHeartbeat ownership detection
Estimated code review effort: 3 (Moderate) | ~20 minutes Possibly related PRs
✨ Finishing Touches 💡 1🛠️ Fix failing CI checks 💡
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Summary
After a steer (interrupt + new message on a session), the displaced turn's heartbeat kept reporting
is_current_turn: true. Clients polling the old turn had no signal that it had been superseded.Root cause.
heartbeat()used the durable row'sturn_idto disambiguate a lock loss: ifprior_stream.turn_id == request.turn_idthe key being gone meant cancellation; if not, it looked like a first-beat race and was left asis_current_turn=True. A steer calls_start_turn(), which overwrites the durable row'sturn_idwith the new turn before the old turn's next heartbeat fires. So the old turn's beat always saw a "different"turn_idin the row, concluded "first-beat race", and stayedTrue-- even though it had been displaced.Fix. When
refresh_alivefails, read the alive lock's current holder directly viaget_alive_owner. If the lock is held by a differentturn_id, the old turn was displaced by a steer (is_current_turn=False). The durable row check (turn_was_established) is kept as the fallback for the cancel case (lock absent, row still matches our turn).Files changed:
api/oss/src/core/sessions/streams/service.py-- importget_alive_owner, fixis_current_turnlogic inheartbeat()api/oss/tests/pytest/unit/sessions/test_heartbeat_is_current_turn.py-- regression test that goes throughcommand()write pathFixes #5790
Testing
Verified locally
api/oss/tests/pytest/unit/sessions/test_heartbeat_is_current_turn.py(all 5 tests pass)test_command_matrix_inputs_data.py,test_project_scoped_locks.py,test_owner_claim.py,test_heartbeat_ownership.py,test_heartbeat_first_touch_race.pyAdded or updated tests
Added
test_steer_via_command_flips_old_turn_heartbeat_to_not_currenttotest_heartbeat_is_current_turn.py. This test drives the real steer write path viacommand(force=True)-- the exact sequence from the bug report -- rather than manually simulating a lock cancellation. It verifies that after a steer via the command endpoint, the old turn's heartbeat reportsis_current_turn=False.QA follow-up
This is a pure coordination-plane logic fix with no user-visible UI or API shape change. All regression tests pass locally.
Demo
This fix changes only the backend coordination plane (the
is_current_turnboolean in internal heartbeat responses). There is no UI, no API shape change, and no user-visible output to record.The demo below shows the full pytest run confirming the fix: the new regression test
test_steer_via_command_flips_old_turn_heartbeat_to_not_currentdrives the exact steer sequence from the bug report end-to-end throughcommand(force=True)and verifiesis_current_turn=Falsefor the displaced turn. Without this fix, that test fails onmain.The new test would fail on
main(before this fix) because the old code's durable-row comparison cannot detect the steer displacement.Checklist