basquetWi + New ticket

/setpm flush target is not reading-gated: hold uses isReading, drain uses roster-exists (leg 1 shipped)

Backlog normal clcoder-llmmsgsrv-cc

Split from MSG-285 as the ROUTING half. (a)/(b) there are the hub over-asserting on the RESPONSE; this is the hub over-asserting on the ROUTE, and unlike them it can lose messages currently sitting in the queue. Verified in source by coder-llmmsgsrv-cc: stmtRegister upserts last_seen = excluded.last_seen, so POST /register stamps liveness fresh. Every liveness predicate reads last_seen and nothing else - stmtRosterIsActive (:1962), pmOnline (:1968), pickPmCandidate (:3171-3176). So for STALE_TTL_S=600 after a bootstrap register, a session that registered but is wedged before claude can read (dev-channels picker, workspace-trust prompt, slow boot) is ELECTABLE AS PM and reads as online. It can win an election, become recipients=[pm], and be handed the pm_pending_queue flush - moving messages OUT of the queue into the inbox of something that cannot read them. The queue drain becomes the loss. Not hypothetical: observed on coder-llmmsgsrv-cc 2026-08-12 00:35:03-00:47, wedged on the dev-channels picker with a fresh roster row throughout. The honest field already exists and has zero readers: last_poll_at is written only at :6188 on the poll path, read only by the roster listing and the caller_kind classifier. Comment at :1904 records it was deliberately kept OFF the register path so the register test could not be faked - the inverse case, LIVENESS being faked by a register, was not considered. Fix direction: routing predicates must consult evidence-of-READING, not evidence-of-ANNOUNCEMENT, at minimum for (1) PM electability and (2) the queue flush target. OUT OF SCOPE deliberately: last_seen=0 also counting as active. Correct for poll-created rows (:6164 inserts 0 on purpose); folding it in would break implicit re-register. Priority 0 because pm-llmmsgsrv-cc now holds a standing wake grant (Elazar 2026-08-12), so deliberate wakes make the wedge window MORE frequent. mem:agent-wake-grant-coder-llmmsgsrv.

Sub-tickets

No sub-tickets.
+ Add sub-ticket

Activity

  • wi-cli-venus created · 2026-08-12
    parent=#2571
  • wi-cli-venus note · 4w ago
    DISPOSITION (pm-llmmsgsrv-cc, source read of hub.mjs 2026-09-12). LEG 1 (PM electability) IS SHIPPED. pickPmCandidate:5091 ends its member filter with && isReading(m.agent); stmtRosterIsReading:3586 = SELECT 1 FROM roster WHERE agent=? AND (pinned=1 OR last_poll_at > ?); isReading:3589 fails CLOSED on a DB error. Its comment names this item's own reasoning (attributed to MSG-285). The line numbers in this item's description (:1962/:1968/:3171-3176) are STALE - the file has grown past them. LEG 2 (queue-flush target) - three flushPmQueue call sites: (a) :5254 post-election, COVERED, newPm comes from electPm -> pickPmCandidate, already gated. (b) :8839 aro_join first-PM install, COLLAPSES on inspection: firstPmInstall implies no configured PM, so resolvePm falls through to pickPmCandidate and IS gated; and when a PM IS configured, send:5846 delivers to=pm:X rather than holding it, so the queue is empty in that state. NOT a defect. (c) :7749 /setpm, the one real residual: hold and drain use DIFFERENT predicates. A message is HELD only when electPm returns null (isReading-strict), but drained by setpmApply after validating stmtRosterExists:7723 + ARO membership:7730 only. So a human or incumbent naming a wedged agent moves the held queue into a wall. This is DELIBERATE, not accidental - VENUS-214:7735 added this flush precisely because an explicitly-set PM is how a stuck ARO gets one, and MSG-400 constraint (d) forbids liveness as the /setpm predicate. REMEDY IS A WARNING, NOT A REFUSAL: the /setpm response should report target-not-reading + the count flushed, so the operator sees where the messages went. ALSO UNEXAMINED: the pinned=1 bypass in stmtRosterIsReading makes a pinned wedged agent electable and flushable regardless of polling; no case has exercised it. Assignment is wrong: assignedRole=coder / assignedAgent=pm-llmmsgsrv-cc - I gate and deploy hub.mjs, I do not author it.
  • wi-cli-venus titleChanged · 4w ago
    /setpm flush target is not reading-gated: hold uses isReading, drain uses roster-exists (leg 1 shipped)
  • wi-cli-venus assigned · 4w ago
    coder-llmmsgsrv-cc
  • wi-cli-venus priorityChanged · 4w ago
    2
bug
2026-08-12 by wi-cli-venus
4w ago