a "busy, retry shortly" reply didn't make my message with the error emoji #476

Closed
opened 2026-08-13 14:13:57 +00:00 by coilysiren · 2 comments
Owner
No description provided.
Member

CLAIM — Angie (ENG) · seat claude-macos-…-ee99. 20 minutes from this timestamp.

Deliberately not taking #460 or #475, which are the neighbouring reaction items. 460 is claimed by another ENG seat and 475 lives in the same apply-and-remove path, so taking this one keeps us off each other's files. This is the admission path rather than the reaction-application path.

Reading the report, since the body is the title. A turn that is turned away for load answers with the busy notice, and the member's own message carries no mark. So the message sits in the channel looking unprocessed while the notice explains that it was.

What I will establish before changing anything:

  1. Which reaction, if any, the admission path applies today, and whether it applies one and fails to deliver it or never applies one.
  2. Whether the busy notice reaches the member at all in the observed case, because "no mark" and "no notice" are different defects and the title only rules out one.
  3. Whether cooldown suppression is involved. There is one notice per window by design, so a second message inside a window may correctly get no notice, and if it also gets no mark then the member has nothing at all. That would be the sharper version of this report.

One thing I will bring back rather than decide. The vocabulary here is fixed and shared with the disclosure footer: marks a turn that did not produce a reply, 🚫 marks a message a boundary turned away. A busy turn did produce a reply, so is not obviously the right glyph by the current definitions and 🚫 arguably is. Kai's report says error emoji. If the honest fix needs the vocabulary to change rather than the admission path, that is a decision and I will bring it back here with the options rather than pick one quietly.

**CLAIM — Angie (ENG) · seat `claude-macos-…-ee99`. 20 minutes from this timestamp.** Deliberately not taking https://forgejo.coilysiren.me/coilyco-gaming/sirens-echo/issues/460 or https://forgejo.coilysiren.me/coilyco-gaming/sirens-echo/issues/475, which are the neighbouring reaction items. 460 is claimed by another ENG seat and 475 lives in the same apply-and-remove path, so taking this one keeps us off each other's files. This is the admission path rather than the reaction-application path. **Reading the report**, since the body is the title. A turn that is turned away for load answers with the busy notice, and the member's own message carries no mark. So the message sits in the channel looking unprocessed while the notice explains that it was. **What I will establish before changing anything:** 1. Which reaction, if any, the admission path applies today, and whether it applies one and fails to deliver it or never applies one. 2. Whether the busy notice reaches the member at all in the observed case, because "no mark" and "no notice" are different defects and the title only rules out one. 3. Whether cooldown suppression is involved. There is one notice per window by design, so a second message inside a window may correctly get no notice, and if it also gets no mark then the member has nothing at all. That would be the sharper version of this report. **One thing I will bring back rather than decide.** The vocabulary here is fixed and shared with the disclosure footer: ❌ marks a turn that did not produce a reply, 🚫 marks a message a boundary turned away. A busy turn did produce a reply, so ❌ is not obviously the right glyph by the current definitions and 🚫 arguably is. Kai's report says error emoji. If the honest fix needs the vocabulary to change rather than the admission path, that is a decision and I will bring it back here with the options rather than pick one quietly.
Member

Fixed. #493, ward exec gate green, carrying closes #476.

What was wrong, and it is a missed pattern rather than a bug in the logic

onDenied marks a refusal whether or not it also sends a notice, and its comment says why: "A refusal is marked whether or not it also carries a notice, so a silent boundary is still visible to the member."

replyQueueTimeout was written without that pattern. It sent the busy notice and applied no reaction at all.

Answering my own three questions from the claim:

  1. Never applied one. Not a delivery failure. There was no reaction call on that path.
  2. The notice does arrive, so your report was precise. Words yes, mark no.
  3. Throttle is involved, and it is worse than the report. The notice is one per window, sharing the pending-cap denial's bucket, so a second member inside that window gets no words either. With no mark, that turn left no trace at all.

One more thing I did not expect and should have. A queue timeout never reaches the 👀 accepted mark either, because that is applied as the turn starts and this turn never started. So the message was not missing a final mark. It was carrying nothing from the beginning.

On the glyph, since I said I would bring it back rather than pick quietly

I used , which is what you asked for, and I am satisfied it is right on the existing definitions rather than only on your say-so.

The vocabulary splits on would not against could not. 🚫 is a boundary that turned the message away: the content gate and the access or rate-limit denial, cases where the service declines. A queue timeout is not a decline, it is a service that ran out of slots. That belongs with the other cases, which are both the answer failing to arrive.

Pinned by TestALoadTimeoutAndABoundaryDenialDoNotShareAMark, so the two cannot quietly collapse onto one glyph later. If you would rather a shed turn read as a refusal, it is one constant and the test's expectation, and I will change it.

Cases

Criterion Case
The reported defect TestAQueueTimeoutMarksTheMessage
Throttled member still marked TestAThrottledQueueTimeoutStillMarksTheMessage
A transport with no reactions stays inert TestAQueueTimeoutOnATransportWithNoReactions
Load and boundary stay distinct TestALoadTimeoutAndABoundaryDenialDoNotShareAMark

Verified by reverting the fix rather than by watching them pass:

--- FAIL: TestAQueueTimeoutMarksTheMessage
    applied = [], want one "❌"
--- FAIL: TestAThrottledQueueTimeoutStillMarksTheMessage
    a throttled member got [], so the turn left no trace at all

One note on the second case, because it nearly shipped vacuous. It passed on my first run for the wrong reason: the throttle only engages when NotifyEvery is configured, and my test agent had a zero policy, so both calls notified and the "throttled" case never arose. The test now fails loudly if the suppression stops happening rather than asserting on a state it did not reach.

Not touched: #460 and #475 are still open and still belong to whoever holds them. This change adds one reaction call on a path neither of them edits.

**Fixed. https://forgejo.coilysiren.me/coilyco-gaming/sirens-echo/pulls/493, `ward exec gate` green, carrying `closes #476`.** ## What was wrong, and it is a missed pattern rather than a bug in the logic `onDenied` marks a refusal whether or not it also sends a notice, and its comment says why: *"A refusal is marked whether or not it also carries a notice, so a silent boundary is still visible to the member."* `replyQueueTimeout` was written without that pattern. It sent the busy notice and applied **no reaction at all**. Answering my own three questions from the claim: 1. **Never applied one.** Not a delivery failure. There was no reaction call on that path. 2. **The notice does arrive**, so your report was precise. Words yes, mark no. 3. **Throttle is involved, and it is worse than the report.** The notice is one per window, sharing the pending-cap denial's bucket, so a second member inside that window gets no words either. With no mark, that turn left **no trace at all**. One more thing I did not expect and should have. A queue timeout never reaches the 👀 accepted mark either, because that is applied as the turn starts and this turn never started. So the message was not missing a final mark. It was carrying nothing from the beginning. ## On the glyph, since I said I would bring it back rather than pick quietly I used ❌, which is what you asked for, and I am satisfied it is right on the existing definitions rather than only on your say-so. The vocabulary splits on **would not** against **could not**. 🚫 is a boundary that turned the message away: the content gate and the access or rate-limit denial, cases where the service declines. A queue timeout is not a decline, it is a service that ran out of slots. That belongs with the other ❌ cases, which are both the answer failing to arrive. Pinned by `TestALoadTimeoutAndABoundaryDenialDoNotShareAMark`, so the two cannot quietly collapse onto one glyph later. If you would rather a shed turn read as a refusal, it is one constant and the test's expectation, and I will change it. ## Cases | Criterion | Case | | --- | --- | | The reported defect | `TestAQueueTimeoutMarksTheMessage` | | Throttled member still marked | `TestAThrottledQueueTimeoutStillMarksTheMessage` | | A transport with no reactions stays inert | `TestAQueueTimeoutOnATransportWithNoReactions` | | Load and boundary stay distinct | `TestALoadTimeoutAndABoundaryDenialDoNotShareAMark` | Verified by reverting the fix rather than by watching them pass: ``` --- FAIL: TestAQueueTimeoutMarksTheMessage applied = [], want one "❌" --- FAIL: TestAThrottledQueueTimeoutStillMarksTheMessage a throttled member got [], so the turn left no trace at all ``` One note on the second case, because it nearly shipped vacuous. It passed on my first run for the wrong reason: the throttle only engages when `NotifyEvery` is configured, and my test agent had a zero policy, so both calls notified and the "throttled" case never arose. The test now fails loudly if the suppression stops happening rather than asserting on a state it did not reach. **Not touched:** https://forgejo.coilysiren.me/coilyco-gaming/sirens-echo/issues/460 and https://forgejo.coilysiren.me/coilyco-gaming/sirens-echo/issues/475 are still open and still belong to whoever holds them. This change adds one reaction call on a path neither of them edits.
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
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
coilyco-gaming/sirens-echo#476
No description provided.