Skip to content

emrg: a boss call that times out still locks the owner out for four hours #2499

Closed

Description

@how2how2how2-arch

A boss call that times out still locks the owner out for four hours

What is observed, and where

emrg/server/daemon.py, in _run_boss_child (~line 8137): the owner clock is stamped before the owner's turn runs.

child.set_boss_asked_at(asked_at)

That position was chosen deliberately — a turn that dies mid-way must leave the clock written, so the next call does not pay twice for the same judgement. But the stamp is now written on a path that delivers nothing: _run_boss_tool (~line 8070) cancels the owner's turn when asyncio.wait(..., timeout=timeout_s) expires and returns

{"error": "boss: the owner did not answer within …s, so its turn was
 cancelled. Nothing from it was delivered. … Raise `timeout_s` if the
 owner needs longer to read this session's history."}

The clock is already written by then, so the very next call — including the retry with a larger timeout_s that this error message tells the caller to make — returns the busy notice instead of starting a turn, for REASK_WINDOW_S (four hours).

Why that is a defect and not a cost

The four-hour window exists because a direction that was just given stands: a second call inside it re-buys a judgement that cannot have moved. A timed-out call gave no direction at all — it is the one outcome after which the judgement has not been bought yet. So the window is applied exactly where its own justification is absent, and the failure is not the delay alone:

  1. The error's own remedy is unusable. It says to raise timeout_s; raising it cannot help, because no turn starts.
  2. The busy notice says "carry on with the plan you already have, or finish and report" — true for the intended case, false here: the round asked for direction, was refused, and is told to proceed as if it had one.
  3. It is sticky. The clock lives in the owner session's meta.json, so it survives a daemon restart (that is its purpose), and nothing in the tool can clear it: there is no argument, no command, and no reader that reports "a call timed out and nothing was delivered" as distinct from "a stance was given four minutes ago".

Measured on this checkout: tests/test_boss_tool.py has no test that calls twice across a timeout — the six window tests cover the answered case, the rebuilt session and the turn that raised, but not the turn that ran out of time.

What is expected instead

A call that delivered nothing must not consume the window.

Situation Behaviour
The owner answered the clock stands; the window applies
The owner's turn raised the clock stands (a died turn must not be re-bought)
The owner's turn was cancelled by timeout_s the clock must not stand — the next call starts a turn
A busy notice says no direction was decided (already true)
The timeout error message must not advise a retry the window refuses

What the fix must satisfy

  • The retry after a timeout starts a turn. The decisive reading is a second call after a timed-out first one that observes a turn was started — not one that reads a flag.
  • The died-turn case keeps its guarantee. A turn that raises still leaves the clock written; the existing test for that must stay green, so the two paths are told apart rather than the stamp moved wholesale after the turn.
  • The distinction is in the clock, not only in the message. A fix that rewrites the timeout text but leaves the stamp is not a fix: the window is what refuses the retry.
  • No new state that a restart loses. If the fix needs "the last call delivered nothing", it belongs beside boss_asked_at in meta.json, with the same treatment of a malformed value that boss_asked_at gets.
  • A repository test asserts the retry-after-timeout path, and a test asserts the died-turn path still holds the clock.

Not claimed

The four-hour figure itself is not in question here, and this issue proposes no change to it. The macOS case is not claimed: Test runs ubuntu-latest and windows-2025, and the logic is platform-free Python.

Activity

  1. how2how2how2-arch commented on Oct 22, 2026

    @how2how2how2-arch
    CollaboratorAuthor

    Handled by #2502

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions