fix(bot-screen): no-op lease transitions do not bump the epoch
release() on an agent-held lease and a same-viewer re-acquire rewrote the record and bumped the epoch, so an admitted in-flight agent action looked overtaken and was voided by a double-clicked Hand back or a stray CLI stop; the re-acquire also reset `since` and wiped the on-screen reason.
This commit is contained in:
@@ -193,3 +193,16 @@ def test_lease_files_are_private_even_when_the_lease_is_written_before_the_scree
|
||||
for path in (sd, sd / "lease.json", sd / "lease.lock"):
|
||||
assert path.exists(), path
|
||||
assert stat.S_IMODE(path.stat().st_mode) & 0o077 == 0, f"{path.name} is {oct(path.stat().st_mode)}"
|
||||
|
||||
|
||||
def test_no_op_transitions_do_not_bump_the_epoch():
|
||||
"""Callers void an admitted in-flight action when the epoch moved. A release on an agent-held lease
|
||||
(double-clicked Hand back, a stray CLI stop) or the same viewer re-acquiring changes nothing real, so
|
||||
it must not make a legitimate agent action look overtaken."""
|
||||
e0 = lease.get().epoch
|
||||
assert lease.release().epoch == e0
|
||||
got = lease.acquire("v1", reason="log in please")
|
||||
assert got.epoch == e0 + 1
|
||||
again = lease.acquire("v1")
|
||||
assert again.epoch == got.epoch and again.reason == "log in please" and again.since == got.since
|
||||
assert lease.acquire("v2").epoch == got.epoch + 1 # a different viewer IS a transition
|
||||
|
||||
@@ -170,6 +170,8 @@ def acquire(viewer_id: str, *, profile_key: Optional[str] = None, reason: str =
|
||||
"""Human ``viewer_id`` takes control. Last writer wins: a second viewer evicts the first, and the
|
||||
RFB bridge closes the evicted socket so its UI drops to view-only."""
|
||||
def _m(lease: Lease) -> bool:
|
||||
if lease.holder == HUMAN and lease.viewer_id == viewer_id:
|
||||
return False # already theirs: no epoch bump, `since` and the reason on screen stay put
|
||||
# The agent's ask ("please log in to X") stays as the takeover reason: the human needs it
|
||||
# on screen WHILE they act, not only before they clicked Take over.
|
||||
lease.holder, lease.viewer_id, lease.since = HUMAN, viewer_id, time.time()
|
||||
@@ -194,6 +196,10 @@ def release(viewer_id: Optional[str] = None, *, profile_key: Optional[str] = Non
|
||||
# caller that never inspects the return value leaves a trace.
|
||||
logger.info("bot-desktop lease: release by %r ignored, another viewer holds", viewer_id)
|
||||
return False
|
||||
if lease.holder == AGENT:
|
||||
# Already the agent's. Bumping the epoch here would make a legitimately admitted in-flight
|
||||
# agent action (a double-clicked Hand back, a stray CLI stop) look overtaken and get voided.
|
||||
return False
|
||||
lease.holder, lease.viewer_id, lease.since, lease.reason = AGENT, None, time.time(), ""
|
||||
return True
|
||||
return _transition(profile_key, _m)
|
||||
|
||||
Reference in New Issue
Block a user