Problem
#1573 (PR #1577) made mailbox delivery hold a row when the message's header never appears on the receiving terminal, so a lost message is retried instead of being falsely reported delivered. That is the right direction of error — but nothing bounds the retrying.
If a harness is systematically unverifiable, every clean-gate pass writes the same message again, forever. The agent sees the instruction re-typed once per turn, and the row never resolves.
The known way to get there: a harness rendering on the alternate screen buffer (agy boots into it) has no scrollback, so a message longer than one viewport scrolls its own header out of reach and can never be confirmed. A second, narrower path compounds it: a very long write can evict the pre-write copy from the 1000-line mirror, so the occurrence count comes back equal rather than greater and a genuine delivery reads as unconfirmed.
#1573 assumed this would be loud rather than looping, because held rows escalate. Escalation is visibility only — markEscalated sets a flag and fires an SSE event; it does not stop the drainer from re-delivering. Two CMAP reviewers flagged the gap on PR #1577 and it is recorded there as a known residual.
Suggested fix
Bound the attempts. The obvious shapes, cheapest first:
- An in-process attempt counter in the delivery module (a
Map keyed by row id, pruned with the held set). After N unconfirmed writes, stop rewriting and leave the row held + escalated, or mark it delivered with a loud log. No schema change; lost across a Tower restart, which is acceptable for a bound.
- An
attempts column on the mailbox table — durable, but a migration.
Either way the choice to make explicitly is what happens at the bound: stop retrying and stay held (the agent may never get the message, but nothing is spammed) versus accept and mark delivered (at most N duplicates, then today's pre-#1573 behaviour, loudly logged). The sender is not misled either way — the afx send response already reports held on the first unconfirmed attempt.
Worth measuring agy's actual behaviour first: it was not measurable during #1573 (unauthenticated in that environment), so the headline trigger is inferred from its alternate-screen boot, not observed.
Refs #1573, PR #1577.
Problem
#1573 (PR #1577) made mailbox delivery hold a row when the message's header never appears on the receiving terminal, so a lost message is retried instead of being falsely reported delivered. That is the right direction of error — but nothing bounds the retrying.
If a harness is systematically unverifiable, every clean-gate pass writes the same message again, forever. The agent sees the instruction re-typed once per turn, and the row never resolves.
The known way to get there: a harness rendering on the alternate screen buffer (agy boots into it) has no scrollback, so a message longer than one viewport scrolls its own header out of reach and can never be confirmed. A second, narrower path compounds it: a very long write can evict the pre-write copy from the 1000-line mirror, so the occurrence count comes back equal rather than greater and a genuine delivery reads as unconfirmed.
#1573 assumed this would be loud rather than looping, because held rows escalate. Escalation is visibility only —
markEscalatedsets a flag and fires an SSE event; it does not stop the drainer from re-delivering. Two CMAP reviewers flagged the gap on PR #1577 and it is recorded there as a known residual.Suggested fix
Bound the attempts. The obvious shapes, cheapest first:
Mapkeyed by row id, pruned with the held set). After N unconfirmed writes, stop rewriting and leave the row held + escalated, or mark it delivered with a loud log. No schema change; lost across a Tower restart, which is acceptable for a bound.attemptscolumn on themailboxtable — durable, but a migration.Either way the choice to make explicitly is what happens at the bound: stop retrying and stay held (the agent may never get the message, but nothing is spammed) versus accept and mark delivered (at most N duplicates, then today's pre-#1573 behaviour, loudly logged). The sender is not misled either way — the
afx sendresponse already reportsheldon the first unconfirmed attempt.Worth measuring agy's actual behaviour first: it was not measurable during #1573 (unauthenticated in that environment), so the headline trigger is inferred from its alternate-screen boot, not observed.
Refs #1573, PR #1577.