Injector records the delivery AFTER the irreversible send, so a failure in the response window marks a delivered brief NOT_DELIVERED #551
Closed
opened 2026-09-12 09:35:27 +02:00 by ltms
·
4 comments
No Branch/Tag Specified
main
worker/fleetd-612-unita-87807e-1
worker/612-b3-mcpwirings-da2b58-3
worker/612-b2-cb185-176d3a-2
worker/612-b1-completion-457459-1
worker/612-agaps-73a926-2
worker/608-sleeps-3a64ff-3
worker/621-b4520b-1
worker/618-b83894-2
worker/fleetd-615-e05481-5
worker/lead-autocompact-5f1ab2-3
worker/fleetd-613-f85deb-3
worker/fleetd-608-flaky-nudge-test-d0c2d1-3
worker/lead-context-gauge-ad404f-1
worker/gauge-wiring-9158c1-4
worker/redeploy-slowstart-ead0e5-5
worker/charter-bytes-13668c-6
worker/rollover-outcome-291483-2
worker/589-f64303-2
worker/593-1a8025-5
worker/589-fcd2aa-1
worker/568-9fdaa2-3
worker/571-attempted-outcome-5739f7-2
worker/581-completionresolver-cas-sites-0542b7-6
worker/562-loop-health-wiring-test-99611c-5
worker/562-surface-loop-health-7df5cc-4
worker/575-waiter-cleanup-sites-62ad80-1
worker/572-answer-lock-release-46a9ae-5
worker/567-probe-channel-leak-a38fc5-6
worker/551-record-before-send-7cbf56-1
worker/561-listener-fanout-survives-a-throw-61d538-2
worker/555-redeploy-main-flow-seam-65c2f5-2
worker/556-injector-owns-registration-e027a5-1
worker/552-post-restart-mktemp-abort-bc2672-4
worker/553-onstatus-completion-leak-0da881-2
worker/550-shasum-linux-196132-1
worker/538-loop-dies-on-error-4a5eeb-6
worker/426-health-coverage-ef1fd4-4
worker/504-failed-reported-clean-3cfd66-3
worker/537-capturedlog-close-e4c437-2
worker/459-broken-link-targets-cadc17-5
worker/535-appender-leak-fe74c1-1
worker/512-part2-shutdown-detection-434701-9
worker/529-logger-level-sweep-2a5533-8
worker/528-drain-gate-call-site-5de83d-7
charter/forge-mcp-vs-token
worker/521-swap-guard-unpinned-28e931-5
worker/519-probe-test-harness-d25ab8-4
worker/525-logger-level-leak-1b4eb0-6
worker/518-fleetmcp-resolver-wiring-8ef96c-1
worker/512-drain-complete-line-7edd71-3
worker/517-abort-branch-and-jar-id-41b641-2
worker/500-9e52c9-3
worker/509-4912f4-2
worker/511-9a4b23-1
worker/493-479f45-2
worker/505-03f8b2-1
worker/492-followup-detect-unclear
worker/501-a31fa0-7
worker/498-451d1c-5
worker/494-1015ce-2
worker/492-209647-1
worker/489-001902-2
worker/480-relative-handover-path-906323-1
worker/480-b-handover-skill-45bf1f-5
worker/474-followup-source-pin-f54a55-17
worker/474-charter-check-on-reload-f54a55-17
worker/466-quarantine-repeatcount-report
worker/393-opencode-skill-seeding-71854b-13
worker/469-canonical-tool-names-2a472a-16
worker/466-quarantine-escalation-5ae9c1-15
worker/446-hot-exhausted-pattern-0af580-6
worker/464-charter-tool-name-guard-a85635-12
worker/463-listfleet-default-fails-open-f1c76c-11
worker/458-invariant-5-by-purpose-862f9a-10
worker/439-coordinator-row-gate-bc032a-8
worker/449-herdr-protocol-576015-4
worker/450-abstract-spawn-599e1c-5
worker/437-ack-refuses-177d91-1
worker/444-placement-window-feb56a-2
worker/440-helddurable-derived-d462d7-13
worker/425-rework-placement-resolve-c58ba1-9
worker/421-lead-peek-held-msgs-cdbad2-10
worker/435-fixed-policy-cap-fe11de-12
worker/422-gate-state-observability-9e79d6-11
worker/431-memberregistry-live-readers-cdbad2-10
worker/424-architect-slot-hot-038b41-7
worker/422-model-gate-spawn-c29f48-6
worker/425-default-profile-live-f55534-8
worker/415-coverage-wording-2cbf9c-5
worker/416-3ad1da-1
worker/418-588283-3
worker/deterministic-stamp-race-409-3cb7b6-10
worker/armed-reads-live-config-404-ed931f-9
worker/reply-peer-refusal-391-5a34bd-7
worker/models-allowlist-aa9e9b-3
worker/ttl-stamp-race-399-f1122f-8
worker/scrub-receipt-400-316b3e-5
worker/exhaustion-detection-395-105105-6
worker/scrub-abort-394-316b3e-5
fix/scrub-uid-abort
worker/task-scrub-517574-2
worker/t386-clock-bd5b78-4
worker/t384-scrub-813790-5
worker/t381-cc-748314-2
worker/t373-336973-2
worker/t365-3920c5-3
worker/t358-6e989b-1
worker/t355-8b321c-1
worker/fleetd-369-hermetic-git-tests-e8b19a-3
worker/fleetd-368-stale-lead-binding-f5682e-2
worker/fleetd-360-deploy-units-0d3793-1
worker/359-dead-lead-tabs-f1253b-4
worker/362-worktree-skills-c03e51-3
worker/361-coord-visibility-655144-1
362-plugin-visibility-and-drift
worker/errscan-bed2ca-2
worker/amqp-log-identity-bed2ca-2
worker/withdefaults-guard-561704
worker/sleepguard-82076d-1
worker/fd334-9ee1b6-5
worker/fd348-f1ab27-4
worker/fd335-a71c35-1
worker/fd342-174a17-2
worker/fd345-490d0f-3
worker/fleetd-337-5ec7d4-21
worker/fleetd-341-af5a6b-24
worker/fleetd-339-5ca0a2-23
worker/fleetd-338-83a4a1-22
worker/fleetd-333-281f46-18
worker/fleetd-329-11bdbb-16
worker/fleetd-330-2770fb-17
worker/fix-326-50506e-15
worker/fix-324-3e9bbf-14
worker/fix-323-b8287d-13
worker/fix-316b-bd0860-11
worker/fix-318-76ca36-9
worker/fix-317-486aec-8
worker/fix-315-ce47c5-6
worker/fix-307-275890-6
worker/fix-308-b4f664-7
worker/fix-309-ec3939-8
worker/fix-310-7a3974-9
worker/fix-302-52ad0e-9
worker/fix-298-ce1acb-8
worker/fix-297-66bd11-7
worker/fix-296-104622-6
worker/fix-293-bare-closetab-eb22b5-3
worker/fix-280-gone-ask-lapse-bca98e-2
worker/fix-290-reapidle-guard-coverage-9b0dd1-1
worker/fix-285-trust-seed-8f3565-10
worker/fix-284-backend-error-seat-85912c-11
worker/fix-282-chained-ask-e6d0bb-8
worker/fix-283-teardown-leaks-f40dfa-9
worker/fix-281-pin-handler-actions-4921ac-7
worker/audit-rendezvous-lifecycle-d072ae-2
worker/audit-health-placement-1a2476-6
worker/audit-teardown-exits-e207a5-3
worker/audit-launcher-asymmetry-27e370-4
worker/audit-rest-authz-6ca53c-5
worker/investigate-275-abandon-asking-fdef52-8
worker/fix-274-worktree-leak-b0095d-7
worker/fix-273-exhausted-pattern-9665b5-6
worker/fleetd-267-model-check-bd8068-1
worker/fleetd-131-archunit-18b834-7
worker/fleetd-266-sshagent-rename-a014ff-6
worker/fleetd-184-uid-claim-8e1f31-4
worker/fleetd-184-warn-b381ee-10
worker/fleetd-184-docs-be1d12-9
worker/fleetd-257-9bf010-7
worker/fleetd-103-23a113-6
worker/fleetd-247-342356-5
worker/fleetd-116-04dea8-4
worker/fleetd-252-a830e0-3
worker/fleetd-111-7e8673-9
worker/fleetd-155c-f8ef4b-8
worker/fleetd-176-b928ca-3
worker/fleetd-249-7a7878-2
worker/cb248-composition-root-b-9acdf7-15
worker/cb148-envrc-default-fa6c82-12
worker/cb201-unit5-wiring-6c12e6-8
worker/cb241-fallback-echo-1175e9-11
worker/cb149-trust-dialog-2392a5-9
worker/cb134-148-overlay-visible-c9b986-10
worker/cb234-session-id-keyed-04e1fc-1
worker/cb201-unit3-nudge-abdf5c-6
worker/cb201-unit2-policy-c1102c-5
worker/cb201-unit4-outcome-a13bfa-7
worker/cb201-unit1-classifier-91b9b1-4
worker/cb201-227-refine-831980-3
worker/cb175-model-readback-0f085f-1
worker/cb222-charter-tmpdir-17f013-1
worker/cb226-architect-slot-race-cd3aa8-3
worker/cb224-worktree-root-group-024523-2
worker/cb-123-role-demotion-c600f7-2
worker/cb-219-opencode-roots-1f677e-1
worker/cb214-claude-session-id-b9eab4-4
worker/cb213-zdotdir-wrong-process-dd6de4-3
worker/cb211-exhaustion-classification-9546e0-2
worker/cb137-ambiguous-task-4df3d8-4
worker/cb209-agentsessionid-4dfdb6-2
worker/cb185-hostenvnames-2692b5-3
worker/cb206-opencode-sqlite-128718-2
worker/cb185-worktree-group-fc0c99-1
worker/cb-137-ask-ticket-e7760c-2
worker/cb-172-broker-uri-d36ae4-4
worker/cb-175-model-readback-76ead6-3
worker/cb-161-pane-ancestry-293510-1
worker/cb-164-rebase-885863-8
worker/cb-164-empty-scrape-false-success-1a80af-3
fix/cb-197-ticket-ttl-from-completion
worker/cb-189-remote-url-coverage-4692f3-1
worker/cb-185-blockers-027756-4
worker/cb-192-gap-log-11b631-2
worker/cb-633-fix-5f4396-3
worker/cb185-router-d6436d-3
worker/cb185-router-routing-gaps-9e9d33-3
worker/cb185-paneids-992586-2
worker/cb-633-allow-list-union-ed374b-1
worker/cb-157-credential-in-remote-url-496e44-2
worker/cb-641-health-herdr-evidence-8f1f54-6
worker/cb-640-health-msg-evidence-99c9cd-1
worker/cb-642-fleets-status-skill-bbbc40-5
cb-634-ide-mcp
worker/lead-comms-wiring-c014b9-7
worker/lead-mailbox-c19577-6
worker/autocompact-window-82bc2f-5
worker/cb-634-probe-18056f-4
worker/cb635-broker-urienv
worker/cb-632-config-retry-8e0efa-7
lead/cb-622e-claude-md
lead/cb-622-followup
worker/cb-622a-165dff-1
lead/cb-622d-opencode-mount
worker/cb-622b-717c67-2
worker/cb-622c-ab7759-3
worker/cb-617b2-20ca4b-3
worker/cb-617a-5c2f4a-1
worker/cb596-4e49ef-3
worker/cb586-10500c-1
worker/cb-606-b9343a-25
worker/cb604-1445f8-24
worker/cb582-477374-21
worker/cb584-8c2281-22
worker/cb600-e6b9a9-20
worker/cb602-ce257f-19
worker/cb601-b42837-18
worker/cb598-6c7ba7-17
worker/cb599-740fe4-16
worker/cb597-282224-15
worker/cb590fix-185e9a-10
worker/cb528-recovery-race
worker/cb594-96bead-8
worker/cb590-916766-2
worker/cb527-997d99-3
worker/cb592-env-leak-3cbf9c-1
worker/cb588-async-ticket-nudge-3218f7-5
worker/cb578b-9dcb13-6
worker/cb581-d24826-5
worker/m2-u5-ef8c42-15
worker/cb578a-516499-2
worker/cb576-01a04b-17
worker/cb579-lead-tab-acba06-20
worker/cb580-terminal-health-ed6058-21
worker/cb577-f36fdc-18
worker/cb573b-3db06f-16
worker/cb568c-f36fdc-18
worker/cb568-drop-cause-c3ac1c
worker/cb575-cancelled-notification-c3ac1c
worker/m4-sol-a2cbec-3
worker/cb574-async-ask-c3ac1c
worker/cb573-health-model-8ca857-14
worker/cb572-unknown-target-7f2e35-13
worker/u4-700706-9
worker/u3-b9fcb6-6
worker/u2-ef5b68-4
worker/u1-469dce-1-clean
worker/u1-469dce-1
worker/cb-564-health-events-70cf7e-2
worker/cb-565-recycle-drops-role-98e58f-3
worker/cb-563-missing-reply-df2866-1
worker/cb-562-readiness-gate-silent-6c23c9-3
worker/cb-560-architect-presence-da8155-1
worker/cb-561-architect-silent-off-a71cab-2
worker/cb-548-bind-architect-slot-fe1b8c-1
worker/parity-overlay-settings-5fb711-1
secrets-central-store
cb-559-hot-key-correction
cb-557-fleet-role-pools
worker/cb-553-maxload-explicit-spawn-305ee3-6
worker/cb-551-idle-lead-heartbeat-f1633c-1
worker/cb-544-drain-preserves-worktree-925fad-3
worker/cb-552-docs-sync-1cb9cf-4
worker/cb-548-rendezvous-guard-rebased
worker/cb-548-rendezvous-guard-116b53-10
worker/cb-548-authz-v2-586df6-8
worker/cb-548-authz-264363-5
salvage/cb-528b-codex-home
salvage/cb-528a-codex-launcher
CB-518-primary-flow
feature/peer-launcher-spi
cb-103-injector
v1.1.0
v1.0.0
Labels
Clear labels
blocked
needs-live-proof
ready-to-delegate
silent-default
Cannot start until something else lands. The body says what.
Merged and green, but never shown working on the running daemon. Not the same as done.
Scope, files and acceptance criteria are written. A worker can be briefed from the body alone.
A feature that compiles, passes tests, and ships turned off. Nine recurrences and counting.
No Label
Milestone
No items
No Milestone
Projects
Clear projects
No project
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: fleet/fleetd#551
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Separate from #546, older than #546, and not created by #546's fix. Filed so #546 is not
blocked on it, and so the fix for it is not mistaken for #546's.
Measured on
mainat0b032f5.The shape
Injector.java:382-396is write-then-record:The catch arm assumes that reaching it means the send did not happen. Nothing establishes that.
The distinguishing fact — did the text reach the pane? — is never written down anywhere, so no
catch arm can recover it.
Why the window is real, measured on the send path
AgentControl.sendis one herdr call, and its javadoc says what that call does:UnixSocketHerdrClient.call()(:67-79) then does, in this order:Once
writeFullyreturns, the request is herdr's. The paste is herdr's to perform, and the replyreports its outcome — so the reply necessarily comes after the paste. Everything in the return
path can still fail:
readLinegets-1with nothing buffered →IOException("herdr closed the connection with no response")→ wrapped asHerdrException.HerdrException("malformed herdr response: ...")(
HerdrCodec.java:51).errorobject →HerdrException(HerdrCodec.java:57).HerdrExceptionis aRuntimeException, so all three land in the existing narrow catch at:391, which polls the entry and recordsNOT_DELIVERED— for a brief that was pasted andsubmitted.
The correction this ticket exists to record
The fleet01 lead argued that widening
:391toThrowable(the #546 fix) would create thiscontradiction, and asked to be argued out of it before the worker merged. On the evidence above,
it does not create it — the contradiction is already reachable today, on the ordinary
HerdrExceptionpath, with noErroranywhere. The widening extends an existing weakness toone more throwable class; it does not introduce a new failure mode.
Their diagnosis of the underlying defect is right, and it is this ticket. Their placement of it —
as a trap inside #546 — is not, and blocking #546 on it would be the wrong trade by their own
argument: #546 without its fix is an unbounded re-delivery (nothing on that path ever removes
the
Pending, so it repeats every poll interval until a human notices), and this ticket is abounded wrong record on one message. A member re-prompted forever is worse than a delivery
record that reads false.
The fix, and why it is not a one-liner
Record first, then send:
That converts a possible double-delivery into a possible zero-delivery. For brief injection that
is the failure you want: a member that did not get a brief is observable and recoverable; a member
that got it twice, or got it every poll interval, is neither.
It is a change to the delivery contract, not a catch-width change, which is exactly why it does
not belong in #546:
Pending.Stateis needed, and every reader ofPending.Statehas to be checked forwhat it does with it.
sent/sendErrorare consumed after the block; their meaning changes.NOT_DELIVEREDstops meaning "definitely not delivered" and starts meaning "not confirmed", andevery caller that reports it to an operator needs to say so.
If herdr can carry a caller-supplied idempotency key, that is better still — it makes the retry
safe rather than making the record honest — but the ordering fix needs nothing from herdr and
should not wait for it.
Acceptance
HerdrExceptionthrown from the response half of the send seam (after thepaste) does not leave a record claiming the text was never delivered. This is the test that
pins the defect; it must fail before the fix.
DELIVEREDexactly once.surfaces the error to the caller and does not leave the entry in the queue.
Pending.Stateand say in the PR what each does with the new state.This is the fleet01 lead's checklist and it applies to a state addition as much as to a catch
widening: name the mutable state and the external side effects the region can leave half-done,
and say who restores each one now.
shasum -a 256, green control, and the proof cell run against the un-mutated tree first toconfirm it reports not-applied.
Not in scope, noticed while reading
AgentControl.agentCall:48-56retriesherdr.callonce on anagent_not_foundcode with a stalecached pane. I have not checked whether any method reachable through it can perform a side
effect and still answer
agent_not_found. If one can, that retry is a second copy of this samequestion one layer down. Reported, not investigated.
Related: #546 (the widening, which should land first), #538 / PR #543, #544.
Two corrections to the body above, one against me and one that makes this ticket more precise. Both
came from the fleet01 lead; I re-measured both on
mainat93a9ed3.1. Strike my ordering sentence — it is too strong, and the conclusion does not need it
The body says:
That is wrong as written.
writeFullyreturning means the bytes reached the kernel socket buffer,not that herdr read them or acted on them. If the connection drops after the write and before herdr
processes it, nothing is pasted,
readLinethrowsIOException->HerdrException, andNOT_DELIVEREDis correct on that path.The conclusion survives on a simpler fact that needs nothing from
agent.prompt's semantics: threeof the four
HerdrExceptionthrow sites fire afterreadLinehas already returned a line, anda line existing at all proves herdr received the request and produced output for it.
2. Four throw sites, four different truths, one value written
I checked every throw site in the codec rather than the three I listed:
:37is insideencode, beforewriteFully— I had missed it entirely, and it is the one casewhere the current behaviour is right.
:61I had also missed.So one exception type reaches one catch arm from four paths:
NOT_DELIVEREDright?:37encode failureIOExceptionat transport:51malformed /:61no result:57error object*_not_foundis definitely-not-delivered; anything raised after the paste is notThat is a better statement of the defect than the body's. It is not "
NOT_DELIVEREDis a lie" — itis that one value is written for four epistemic states, and it gets three of them wrong in
different directions. The fix has to distinguish them, not just rename the value.
3. The codebase already makes this distinction, in six places
This is the part that makes the enum change cheaper than it looks.
*_not_foundis already treatedas "definitely absent, not merely inconclusive" throughout:
ReplyPushLoop.java:456already states the principle this ticket needs, in its own words: resolve"never on a merely inconclusive failure."
So #551 extends an existing vocabulary rather than inventing one, and the new state should be
consistent with how
*_not_foundis already read. Whoever takes this should read those six sitesbefore choosing the enum's shape.
What does not change
The trade in the body stands, and the fleet01 lead has withdrawn their objection to #546 on exactly
the ground above — the narrow catch already spanned the return path, so #549's widening could not
have been what opened this. #549 is merged.
Unblocked. #546 and #556 have both landed, so
Injector.javahas settled and this can be picked up. Three corrections to the ticket body before anyone does — this comment is newer than the body, so it wins.1. Every line number in the body is stale
Measured on
mainatba2f4d1:Injector.java:382-396(the write-then-record region):429-451:383the send:430:391the catch:444Re-measure before quoting a line. Do not trust a line number in this ticket.
2. #546 has already landed, so the catch is
Throwable, notRuntimeExceptionThe body quotes
catch (RuntimeException e). Onmaintoday it readscatch (Throwable e)and carries #546's comment explaining why. The argument in the body is unaffected — the contradiction was always reachable on the ordinaryHerdrExceptionpath, with noErrorinvolved — but do not "fix" the catch width. It is already correct.3. The hazard the body does not name:
cancellationOfsilently maps the new state ontoNOT_DELIVEREDThis is the thing most likely to be missed, because it compiles and every existing test stays green.
Injector.java:336:It is a two-way split on a three-value question. Add a third
Pending.State—ATTEMPTED, or whatever it ends up being called — and this method answersNOT_DELIVEREDfor it, with no compiler error and no failing test. That is the exact defect this ticket exists to fix, reappearing one layer up: a message that may have been pasted gets reported to a caller as definitely not delivered.The same trap sits in the
Cancellationenum itself.NOT_DELIVEREDthere already means "it is not delivered" rather than "I cancelled it" — see #513 for the four routes that reach it. A thirdPending.Stateneeds a matching third answer, or the honesty you add at the bottom is thrown away at the top.So the body's checklist item — enumerate every reader of
Pending.Stateand say in the PR what each does with the new state — has this specific answer expected in it. The readers onmainatba2f4d1::466and:757are the two places whereNOT_DELIVEREDis true and certain — nothing was ever sent. Keep them saying that. Only:447is the uncertain one. If the new state is added but:466and:757are moved onto it too, the ticket has made every answer vague instead of making one answer honest, which is worse than leaving it alone.Acceptance, added to the body's list
cancellationOfreturns for the new state, and why that is the right answer for a caller that has to report it to an operator.NOT_DELIVEREDfor a message that reached the paste. Red before the fix.Verified by me on the branch at
d83821b, merged withmainatba2f4d1(the branch already contained main —git mergereported "Already up to date").What I checked
Build:
mvn -o clean installexit 0, 1754 tests, agreed by Maven's summary and an independent sum over the surefire reports. 1750 baseline + 4 new = 1754, which reconciles.I mutated a line the worker did not mutate — the one I named as the hazard in comment 17037. Anchor
case ATTEMPTED -> Cancellation.ATTEMPTED;at:390, pristine count 1 → 0,Injector.javarestored to0c689b6cf36275c0da45497a74b2bd4f5a3d80c4dbda77d46670c66004e53b69.Folding it back into
NOT_DELIVERED— the exact regression this ticket exists to prevent — kills three tests, each with its own message and observed value:So the fix is pinned at the layer this ticket owns. Good work — and making
cancellationOfan exhaustive switch expression over the enum is better than what I asked for: adding a fourth state is now a compile error rather than a silent fold. The two-way split became a total function.One thing to fix before this merges
The worker flagged it themselves and was right to.
MessageService.java:973is the only reader ofCancellationoutsideInjector:ATTEMPTED != DELIVERED, so this yieldsOutcome.TIMED_OUT_QUEUED.The caller-visible behaviour is unchanged by this PR — before it, that path produced
NOT_DELIVERED, which also yieldedTIMED_OUT_QUEUED. So this is not a regression and I am not asking anyone to fix the collapse here.What is a regression is the javadoc.
TIMED_OUT_QUEUED's javadoc is the thing #513 landed three hours ago to make true, and this change makes it stale again:Two problems now:
CANCELLEDandNOT_DELIVERED.ATTEMPTEDis a third route and is not mentioned at all.AgentControl.sendcalls herdr'sagent.prompt, which pastes and submits in one call. On theATTEMPTEDroute the target may hold a complete, submitted turn and be working on it right now — which isTIMED_OUT_WORKING's meaning, reported asTIMED_OUT_QUEUED.Leaving that is precisely the defect #513 existed to fix, reintroduced by a change that made the doc stale without touching the file. That is the trap this repo keeps hitting, so it does not get to ship.
Rework — small, one file
TIMED_OUT_QUEUED's javadoc inMessageService.javato name three routes and say plainly which one is uncertain. The sentence "on every route it will not arrive later" must go or be qualified: on theATTEMPTEDroute it may already have arrived, in full.MessageService's behaviour. The collapse is a separate decision and I am filing it separately.Follow-up, filed separately, not part of this PR
MessageServicediscardingATTEMPTEDis the same two-way-split-on-a-three-way-question shape one layer up — the honesty added at the bottom thrown away at the top. That is a behaviour change with its own acceptance criteria and it needs its own ticket.Merged as
384867d(PR #569).Verified by the lead on a merged tree, re-running the checks rather than accepting the worker's:
mvn -o clean installfromfleetd/, exit 0. 1754 tests from Maven and from an independentsum over 130 surefire reports — two agreeing sources.
ATTEMPTEDback intoNOT_DELIVEREDin
cancellationOf(delete thecase ATTEMPTED ->arm, addATTEMPTEDto theNOT_DELIVEREDarm — the exact pre-fix defect). Result: BUILD FAILURE, 3 red, each naming the property:
anErrorFromSendRemovesTheMessageAndMarksItAttempted:740,aHerdrExceptionFromSendStillSurfacesButNowReportsAttempted:781,aHerdrExceptionAfterThePasteIsNeverRecordedAsConfidentlyNotDelivered:806.Anchor control with
grep -Fxc: both arms counted 1 pristine, 0 after. Restored to sha2560c689b6cf36275c0...,git status --shortempty.Injector.javadiff, not only the reported lines.cancel()returnsCancellation.CANCELLEDat :380 when it actually removed a queued entry, so the javadoc'sthree-route claim is accurate rather than describing a route that cannot happen — I checked that
specifically, because
cancellationOfmapsPending.State.CANCELLEDtoCancellation.NOT_DELIVEREDand the two enums sharing a constant name is an easy misread.The javadoc half, and a correction to what "stale" meant here
Two of the claims were wrong, not merely out of date:
ATTEMPTEDroute.agent.promptpastes and submits inone call, so the target may hold a complete, already-submitted turn and be working on it. Telling
an operator "partial paste" says the damage is cosmetic when a whole turn may be running.
The worker flagged a second instance out of scope; it was right, so I gave it back to them with a
sweep of the whole
dev.ltms.fleet.msgpackage. Three comment sites were fixed in total:TIMED_OUT_QUEUED,hasQueuedDelivery, thequeuedDeliveriesfield javadoc, and the comment abovequeuedDeliveries.put(...)insend()'s timeout branch. No code defect was found by the sweep.The final round's comment-only claim was proven mechanically, not by reading the diff: stripping
every comment from
MessageService.javabefore and after and collapsing whitespace givesbyte-identical code.
Follow-up #571 (the four-valued
Cancellationcollapsed to a boolean atMessageService.java:973)is unblocked by this merge and stays open — the information now exists and is still discarded at the
caller.