feat(sessions): configurable per-session exclusivity with queue-instead-of-refuse (#101279) - #103797
Open
salch-cred wants to merge 2 commits into
Open
salch-cred wants to merge 2 commits into
salch-cred wants to merge 2 commits into
Conversation
Related: implements #101279 (Options 1+2). Note this branch also carries the unrelated |
1 task
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Configurable per-session exclusivity for shared-brain deployments (#101279): a new
gateway.per_session_exclusiveconfig switch (defaulttrue— behavior unchanged) that, when set tofalse, turns aSESSION_NOT_OWNEDrefusal into a bounded wait for the current owner to finish, then re-acquire — the queue semantics the messaging gateway already gives Telegram — instead of pushing a mid-day failure onto the second surface.This implements the issue's proposed Option 1 (config switch, default on) + Option 2 (queue instead of refuse) as one coherent design: the switch decides whether the desktop/CLI submit paths queue or refuse.
Changes
hermes_cli/active_sessions.pyper_session_exclusive(config)— resolvesgateway.per_session_exclusive(top-level fallback, bool/"true"/"false" strings accepted, invalid values warn once and keep the safe defaultTrue).wait_for_session_ownership(...)— bounded poll of the live-lease registry until no other lease holds the session (or timeout/abort). Never raises: an unreadable registry returnsFalseso the caller falls back to the original refusal.on_waitfires exactly once for a status line ("Another Hermes process is using this session; waiting...").tui_gateway/session_lifecycle.py—_ensure_active_session_slot: on aSESSION_NOT_OWNEDrefusal, when the operator opted out, wait for the owner and re-acquire (_maybe_queue_for_session_ownership). All other refusal reasons (registry unavailable, capacity) are untouched, and the default path is a single dict check as before.cli.py_claim_active_session— same queue-instead-of-refuse on the CLI surface, with a yellow hourglass notice.tui_gateway/methods_voice.pygateway.capabilities— advertisesper_session_exclusive_queue_mode(config-dependent, separate from the build-enforcedper_session_exclusive_submit) so clients can surface "queued" instead of "refused" only when it is actually true.What does NOT change
tests/test_active_session_exclusivity.pysuite passes untouched).SESSION_COORDINATION_UNAVAILABLE(registry unreadable) still fails closed — queueing is never used to smuggle a second writer through an unknown-ownership state.MAX_CONCURRENT_SESSIONScapacity policy is untouched.LEASE_WAIT_SECONDS); a timeout falls back to the original refusal.PER_SESSION_EXCLUSIVE_SUBMITremains a module constant: the build-level enforcement is unchanged; the config only governs how a surfaced refusal is handled.Tests
tests/test_per_session_exclusive_opt_out.py(13 new):on_waitfires exactly once (not per poll); other sessions' leases are not waited onRegression:
tests/test_active_session_exclusivity.py+tests/hermes_cli/test_active_sessions.py(29) pass unchanged;tests/tui_gateway/test_protocol.py(68) and the cross-process ownership suites (10) pass.ruff checkclean.Closes #101279