Pairing probe polls three times faster than Hermes can answer, and the max-pending refusal is a dead end #39

Closed
opened 2026-07-30 10:20:11 +00:00 by aiko · 1 comment
Owner

PAIRING_POLL_MS = 20_000 (src/renderer/src/services/pairing.ts:83). Hermes rate-limits pairing responses to one per user per 600 seconds (hermes-agent/gateway/pairing.py:48), and the rate-limited path returns None with no message sent (hermes-agent/gateway/run.py:12261-12262).

So 29 of every 30 probes are turns pushed at the gateway that produce nothing observable. The comment above the constant names the ten-minute limit and then sets the interval to twenty seconds anyway.

The user-visible half is worse than the waste: while probes are being dropped there is no note, so RETRY_NOTE (「承認が済めば、こちらで気づく」) is a promise the client can only keep when a probe actually lands.

The dead-end underneath it

MAX_PENDING_PER_PLATFORM = 3 (hermes-agent/gateway/pairing.py:52), and pending codes are pruned only at their one-hour TTL. Three unapproved codes on the denpa platform — reachable in half an hour of reload-and-retry, especially given the code loss in the sibling issue — and Hermes answers the refusal instead of a code. pairingAfter then holds unpaired with code: null (services/pairing.ts:179), the 周波数 gate stays shut, and the only thing on screen is Hermes' English refusal sentence in note. Nothing tells the operator that the fix is to approve or expire an existing code, and nothing recovers for up to an hour.

What is needed

  • Poll at the server's actual cadence, not three times faster than it can answer.
  • Prefer an explicit 「もう一度頼む」 key over any fast automatic poll — asking is a turn, and the human is the one who knows they just walked to the other machine.
  • When a refusal arrives, say what it means in the product's own words rather than surfacing the raw English sentence alone: the platform is holding unapproved codes, and approving or waiting out one of them is the way forward.

Acceptance

  • Probe cadence does not exceed what RATE_LIMIT_SECONDS can answer
  • The wait screen distinguishes "asked, waiting" from "asked, silently rate-limited"
  • The max-pending refusal renders as an explained state with a next action, not only as a foreign sentence
  • No probe is sent from 待受, and none after pairing is proven (both hold today — pin them)
  • Test: the refusal path does not leave the screen claiming a code is coming
`PAIRING_POLL_MS = 20_000` (`src/renderer/src/services/pairing.ts:83`). Hermes rate-limits pairing responses to one per user per **600** seconds (`hermes-agent/gateway/pairing.py:48`), and the rate-limited path returns `None` with no message sent (`hermes-agent/gateway/run.py:12261-12262`). So 29 of every 30 probes are turns pushed at the gateway that produce nothing observable. The comment above the constant names the ten-minute limit and then sets the interval to twenty seconds anyway. The user-visible half is worse than the waste: while probes are being dropped there is no `note`, so `RETRY_NOTE` (「承認が済めば、こちらで気づく」) is a promise the client can only keep when a probe actually lands. ## The dead-end underneath it `MAX_PENDING_PER_PLATFORM = 3` (`hermes-agent/gateway/pairing.py:52`), and pending codes are pruned only at their one-hour TTL. Three unapproved codes on the `denpa` platform — reachable in half an hour of reload-and-retry, especially given the code loss in the sibling issue — and Hermes answers the refusal instead of a code. `pairingAfter` then holds `unpaired` with `code: null` (`services/pairing.ts:179`), the 周波数 gate stays shut, and the only thing on screen is Hermes' English refusal sentence in `note`. Nothing tells the operator that the fix is to approve or expire an existing code, and nothing recovers for up to an hour. ## What is needed - Poll at the server's actual cadence, not three times faster than it can answer. - Prefer an explicit 「もう一度頼む」 key over any fast automatic poll — asking is a turn, and the human is the one who knows they just walked to the other machine. - When a refusal arrives, say what it means in the product's own words rather than surfacing the raw English sentence alone: the platform is holding unapproved codes, and approving or waiting out one of them is the way forward. ## Acceptance - [ ] Probe cadence does not exceed what `RATE_LIMIT_SECONDS` can answer - [ ] The wait screen distinguishes "asked, waiting" from "asked, silently rate-limited" - [ ] The max-pending refusal renders as an explained state with a next action, not only as a foreign sentence - [ ] No probe is sent from 待受, and none after pairing is proven (both hold today — pin them) - [ ] Test: the refusal path does not leave the screen claiming a code is coming
Author
Owner

Superseded by ADR-0011, devices pair to the go-between, not to Hermes (denpa/docs/adr/0011-devices-pair-to-the-go-between.md, commit 04a3249).

Both halves of this issue were about surviving Hermes' pairing budget: a 20-second poll against a 600-second server limit, and the MAX_PENDING_PER_PLATFORM = 3 dead end that a single-user household reaches by onboarding a third or fourth screen.

ADR-0011 removes the budget from the path entirely. The go-between pairs to Hermes once, as one platform user, and mints its own per-device enrollment codes after that. Nothing consumes Hermes' pairing allowance per screen, so there is no cadence to get right and no max-pending refusal to explain.

The decision recorded here still stands and carries into the new flow: asking is a deliberate act, not a poll. Enrollment is requested by the operator — at the server console, or through Hermes as a transport — never on a timer. The 「もう一度頼む」 affordance this issue asked for is subsumed by that, since there is nothing to retry automatically.

Also note the probe loop itself goes away: the client no longer learns its own trust state by sending turns and reading her prose (ADR-0011's second rule).

The work in flight for this was stopped and preserved at cf7d0e0 on issue/denpa-37-39-pairing-slice, unreviewed and unmerged.

Superseded by ADR-0011, *devices pair to the go-between, not to Hermes* (`denpa/docs/adr/0011-devices-pair-to-the-go-between.md`, commit `04a3249`). Both halves of this issue were about surviving Hermes' pairing budget: a 20-second poll against a 600-second server limit, and the `MAX_PENDING_PER_PLATFORM = 3` dead end that a single-user household reaches by onboarding a third or fourth screen. ADR-0011 removes the budget from the path entirely. The go-between pairs to Hermes **once, as one platform user**, and mints its own per-device enrollment codes after that. Nothing consumes Hermes' pairing allowance per screen, so there is no cadence to get right and no max-pending refusal to explain. The decision recorded here still stands and carries into the new flow: **asking is a deliberate act, not a poll.** Enrollment is requested by the operator — at the server console, or through Hermes as a transport — never on a timer. The 「もう一度頼む」 affordance this issue asked for is subsumed by that, since there is nothing to retry automatically. Also note the probe loop itself goes away: the client no longer learns its own trust state by sending turns and reading her prose (ADR-0011's second rule). The work in flight for this was stopped and preserved at `cf7d0e0` on `issue/denpa-37-39-pairing-slice`, unreviewed and unmerged.
aiko 2026-07-30 12:48:02 +00:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
aiko/denpa#39
No description provided.