fleetd #780: queued sends no longer lie to fleet_poll or fleet_send #783

Open
agent wants to merge 1 commits from worker/780-faf58b-3 into main
Member

fleetd #780 — the delivery path to a pane lied about an undelivered message: fleet_poll reported "pending — worker working" for a message still sitting in the injector's queue, never injected, with no accept-time warning and no timeout.

1. fleet_poll wording. MessageService.pendingDetail(target) now checks Injector.queuedWaitMillis(target). If the message is still queued (never attempted), the detail reads:

queued, not yet delivered (target is <live status>; queued <N>s)

Once actually delivered, it reports the plain live status as before (e.g. worker working).

2. Accept-time warning. FleetMcp.sendAsync() now does a synchronous status check at accept time and appends a warning to the accept text when the target is not injectable:

accepted — task delegated. Poll fleet_poll with ticket=<ticket>

Warning: <target> is currently <status>, not idle/blocked/done — this message is queued, not yet delivered, and will wait until the target frees up. Poll fleet_poll to see when it lands.

Chose a warning over a hard refusal, per the brief's stated preference — a brief busy spell is normal and the message does land eventually.

3. Timeout. New named constant Injector.QUEUE_WAIT_GRACE_POLLS = 4800 (~20 minutes at the 250ms poll interval), mirroring READINESS_GRACE_POLLS. A message that sits at the head of a target's queue with no delivery attempt for that long is failed via the same onTurnFailed path a readiness timeout uses — it does not call forget, since the target here is merely busy, not gone.

Tests added (both pairs include a positive control):

  • MessageServiceTest.aQueuedButNeverInjectedMessageDoesNotPollAsWorking / aDeliveredMessageStillPollsAsWorkerWorking
  • InjectorTest.failsAQueuedMessageWhoseTargetNeverFreesUp / aTargetThatFreesUpBeforeTheQueueWaitGraceIsDeliveredNormally

Build: mvn clean install from the worktree root — Tests run: 2179, Failures: 0, Errors: 0, Skipped: 0, BUILD SUCCESS, exit code 0.

Fixed as a side effect: InjectorTest.readinessGraceExpiryLogsTheMeasuredElapsedTimeNotArithmeticOnConstants's strict stub clock needed a third reading — enqueue() now stamps Pending.enqueuedAtMillis, which adds one legitimate nowMillis read ahead of the readiness-grace branch's own two.

Out of scope, not touched: the blocking send() path already reports TIMED_OUT_QUEUED/TIMED_OUT_WORKING after its own caller-supplied timeout and has no separate "accept" message to carry a warning on — left as-is, decided not to add one there.

Could not run: the CLAUDE.md-to-wiki sync check — wiki/ is uninitialized in this worktree (0 entries, confirmed with git submodule status).

fleetd #780 — the delivery path to a pane lied about an undelivered message: `fleet_poll` reported "pending — worker working" for a message still sitting in the injector's queue, never injected, with no accept-time warning and no timeout. **1. `fleet_poll` wording.** `MessageService.pendingDetail(target)` now checks `Injector.queuedWaitMillis(target)`. If the message is still queued (never attempted), the detail reads: ``` queued, not yet delivered (target is <live status>; queued <N>s) ``` Once actually delivered, it reports the plain live status as before (e.g. `worker working`). **2. Accept-time warning.** `FleetMcp.sendAsync()` now does a synchronous status check at accept time and appends a warning to the accept text when the target is not injectable: ``` accepted — task delegated. Poll fleet_poll with ticket=<ticket> Warning: <target> is currently <status>, not idle/blocked/done — this message is queued, not yet delivered, and will wait until the target frees up. Poll fleet_poll to see when it lands. ``` Chose a warning over a hard refusal, per the brief's stated preference — a brief busy spell is normal and the message does land eventually. **3. Timeout.** New named constant `Injector.QUEUE_WAIT_GRACE_POLLS = 4800` (~20 minutes at the 250ms poll interval), mirroring `READINESS_GRACE_POLLS`. A message that sits at the head of a target's queue with no delivery attempt for that long is failed via the same `onTurnFailed` path a readiness timeout uses — it does **not** call `forget`, since the target here is merely busy, not gone. **Tests added** (both pairs include a positive control): - `MessageServiceTest.aQueuedButNeverInjectedMessageDoesNotPollAsWorking` / `aDeliveredMessageStillPollsAsWorkerWorking` - `InjectorTest.failsAQueuedMessageWhoseTargetNeverFreesUp` / `aTargetThatFreesUpBeforeTheQueueWaitGraceIsDeliveredNormally` **Build:** `mvn clean install` from the worktree root — `Tests run: 2179, Failures: 0, Errors: 0, Skipped: 0`, `BUILD SUCCESS`, exit code 0. **Fixed as a side effect:** `InjectorTest.readinessGraceExpiryLogsTheMeasuredElapsedTimeNotArithmeticOnConstants`'s strict stub clock needed a third reading — `enqueue()` now stamps `Pending.enqueuedAtMillis`, which adds one legitimate `nowMillis` read ahead of the readiness-grace branch's own two. **Out of scope, not touched:** the blocking `send()` path already reports `TIMED_OUT_QUEUED`/`TIMED_OUT_WORKING` after its own caller-supplied timeout and has no separate "accept" message to carry a warning on — left as-is, decided not to add one there. **Could not run:** the `CLAUDE.md`-to-wiki sync check — `wiki/` is uninitialized in this worktree (0 entries, confirmed with `git submodule status`).
agent added 1 commit 2026-10-05 19:55:05 +02:00
fleetd #780: tell the truth about a queued-but-undelivered send
CI / shell-tests (pull_request) Failing after 7s
CI / contract (pull_request) Successful in 52s
CI / build (pull_request) Failing after 2m10s
1cc0322782
A send to a pane that never frees up was accepted, then polled as
"pending — worker working" forever, with no WARN and no timeout. That
text is only ever correct for a message the injector already handed
off; a message still sitting in its queue now reports as queued, with
the target's live status and how long it has been waiting.

Injector.enqueue() now stamps Pending.enqueuedAtMillis so
queuedWaitMillis(target) can tell "never attempted" apart from
"delivered, now being worked on". MessageService.pendingDetail() uses
it to pick the poll wording. FleetMcp.sendAsync() adds a best-effort
warning to the accept text when the target is not injectable at send
time, per the brief's stated preference for a warning over a hard
refusal (a short busy spell is normal).

Injector gets a new bound, QUEUE_WAIT_GRACE_POLLS (4800 polls, ~20min
at the 250ms poll interval), mirroring READINESS_GRACE_POLLS: a
message that sits at the head of the queue with no delivery attempt
for that long fails via onTurnFailed, the same path a readiness
timeout uses, without touching presence — the target is busy, not
gone.

Fixed InjectorTest.readinessGraceExpiryLogsTheMeasuredElapsedTimeNotArithmeticOnConstants's
stub clock, which now sees one extra, legitimate nowMillis read from
enqueue()'s new stamp.

Tests: aQueuedButNeverInjectedMessageDoesNotPollAsWorking +
aDeliveredMessageStillPollsAsWorkerWorking (poll wording, with
positive control); failsAQueuedMessageWhoseTargetNeverFreesUp +
aTargetThatFreesUpBeforeTheQueueWaitGraceIsDeliveredNormally (timeout,
with positive control).
Some checks are pending
CI / shell-tests (pull_request) Failing after 7s
CI / contract (pull_request) Successful in 52s
CI / build (pull_request) Failing after 2m10s
This pull request can be merged automatically.
You are not authorized to merge this pull request.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin worker/780-faf58b-3:worker/780-faf58b-3
git checkout worker/780-faf58b-3
Sign in to join this conversation.