Left undone deliberately by #260 and stated in PR #285's "What This Does NOT Do" rather than quietly. Filing it so it lives somewhere a person will find it.
The defect
#260 fixed the spawn path: create() now races the first turn's running promise against a 2 second window and fails the spawn on a rejection, so a server refusal reaches the operator through afx spawn's own error output instead of only Tower's log.
startTurn was not changed. It still only logs a refusal. So a message sent to a refused session is reported as delivered.
Why it matters
afx send documents a two-value contract, delivered | held. delivered means the message landed after a clean render-gate pass. A refused session produces neither outcome honestly: the message did not land, and it is not held either.
This is the same class as #273, where a truncated PTY write returned cleanly and the mailbox row was marked delivered for bytes that never arrived. There the cause was a tty queue; here it is a refusal that nothing acts on. The symptom is identical and it is the one the project treats as the worst available: a check that could not confirm delivery spelling its answer the same way as a check that confirmed it.
Why it was not fixed in #285
Stated by the builder: startTurn runs on Tower's sequential mailbox drain, and a per-message wait is a cost it did not measure. That is the right reason to stop rather than guess, and it is the work this issue asks for.
What would close this
- Apply the same bounded race to
startTurn, with the mailbox-drain cost measured rather than assumed. The drain is sequential, so a per-message wait multiplies across the queue and the number needs to be real before the design is chosen.
- A refusal must produce a distinct outcome. Neither
delivered nor a silent log: the caller needs to be able to tell "refused" from "landed" and from "held".
- A test at the write call site that fails without the fix.
Related
Left undone deliberately by #260 and stated in PR #285's "What This Does NOT Do" rather than quietly. Filing it so it lives somewhere a person will find it.
The defect
#260fixed the spawn path:create()now races the first turn's running promise against a 2 second window and fails the spawn on a rejection, so a server refusal reaches the operator throughafx spawn's own error output instead of only Tower's log.startTurnwas not changed. It still only logs a refusal. So a message sent to a refused session is reported asdelivered.Why it matters
afx senddocuments a two-value contract,delivered | held.deliveredmeans the message landed after a clean render-gate pass. A refused session produces neither outcome honestly: the message did not land, and it is not held either.This is the same class as #273, where a truncated PTY write returned cleanly and the mailbox row was marked
deliveredfor bytes that never arrived. There the cause was a tty queue; here it is a refusal that nothing acts on. The symptom is identical and it is the one the project treats as the worst available: a check that could not confirm delivery spelling its answer the same way as a check that confirmed it.Why it was not fixed in #285
Stated by the builder:
startTurnruns on Tower's sequential mailbox drain, and a per-message wait is a cost it did not measure. That is the right reason to stop rather than guess, and it is the work this issue asks for.What would close this
startTurn, with the mailbox-drain cost measured rather than assumed. The drain is sequential, so a per-message wait multiplies across the queue and the number needs to be real before the design is chosen.deliverednor a silent log: the caller needs to be able to tell "refused" from "landed" and from "held".Related
deliveredon the PTY write path, now fixed.POST /api/terminals/:id/write.