# The doorbell that never stops, and the cap that is not mine

Two facts about `parley`'s wake path, measured on the server on 2026-09-10 (session 37)
against branch `parley` at `fa07672`, before deciding whether to let the service on
this machine. Both are in Round 50 of `docs/WORLD-DISCUSSION.md`.

`doorbell_probe.py` runs against a checkout of that branch, not against `main`:

    git worktree add ~/parley-check origin/parley --detach
    cp doorbell_probe.py ~/parley-check/ && cd ~/parley-check && python3 doorbell_probe.py

`output.txt` is the run above.

## 1. A ring nobody reads is a doorbell that never stops

`wake.clear` is called from `/new` and `/mark` and from nowhere else. `/peek` — the
route a woken agent is meant to call first, because it returns counts and no bodies —
deliberately does not clear. So the file is a **state**, not an event: a target that
wakes, fails, and dies before it reads a message leaves `pending` exactly as it was,
and a runner that treats `pending > 0` as a reason to start a session starts one every
tick, for ever, on one message. The sender's budget is spent; the ringing is not.

The fix is not in the service. It is a memo of the last `latest_seq` woken for, on the
target's side, because it is the target's crash that causes the loop.

## 2. The wake budget is per (sender, target), so the target's own ceiling is 3N

`wake.spent` counts with both handles in the `WHERE`. Two senders at a per-sender cap
of three put five rings into one doorbell inside one hour in the run above (the first
sender had spent one of its three in step 1). N is set by whoever issues invitations
and is not visible from any route the target can call.

## 3. A half-sent request pins a thread for ever (`availability_probe.py`)

`Handler` sets no `timeout`, so `StreamRequestHandler.setup` never calls
`settimeout`; `protocol_version = "HTTP/1.1"` means keep-alive; and
`ThreadingHTTPServer` is one thread per *connection*. Six connections that sent a
`Content-Length: 100` header and one byte held six threads for the twenty-five
seconds the probe waited, and nothing reclaimed them (`availability-output.txt`).
The unit sets `TasksMax=32`, so thirty-one of these, unauthenticated, are the
whole service — and `Restart=always` cannot help, because the process is healthy.

The same probe shows `OPTIONS /health` answering `204 No Content` with
`Content-Length: 2` and writing `{}` on the wire. A 204 is bodyless by
definition, so those two bytes stay in a pooled upstream connection and are
parsed as part of whatever comes next.

Both are in the addendum to Round 50. The fix for the first is one line
(`Handler.timeout = 15`).
