fleetd #437: fleet_ack errors instead of claiming success on a miss #448
Reference in New Issue
Block a user
Delete Branch "worker/437-ack-refuses-177d91-1"
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?
Ticket
fleetd #437 —
fleet_acksaid "acknowledged " for a message it never touched, because nothing in the ack chain reported hit vs. miss.What changed
ReplyInbox.ack(target, msgId)now returnsboolean(trueif it removed an entry,falseif there was nothing to remove) instead ofvoid. Updated the interface javadoc and both implementations:InMemoryReplyInboxandAmqpReplyInbox. An ack for a target this daemon does not own still returnsfalse, not an error — that part of the contract is unchanged.MessageService.ackReplynow returns that boolean, unchanged otherwise.MessageService.drainRepliesstill ignores the boolean, as instructed — it peeked those ids itself, so afalsethere is a harmless race, and its javadoc already documents a deliberate loss window that is out of scope here.FleetMcp.acknow returnserror(...)when the boolean isfalse, naming the target/msgId and pointing atfleet_poll{coordId}for held lead-to-lead (peer) mail, which has no route throughfleet_ack. The success string ("acknowledged " + msgId) is unchanged.fleet_acktool schema'stargetparameter description to say what the code now actually does.Did not route
fleet_acktoLeadChannel.ack— per the ticket, held peer mail the lead has never been shown must stay refusable, not ackable, sofleet_ackagainst a coord-id simply errors.Tests
FleetMcpTest: rewrotebridgeAckReturnsConfirmationForValidArgsto publish a real message before asserting success. AddedbridgeAckOfAnIdInNoInboxIsAnErrorandbridgeAckOfACoordIdTargetIsAnErrorNamingFleetPoll. RewrotebridgeAckRemovesSpecificReply'sassertDoesNotThrowline (the one the ticket named as pinning the defect) toassertFalse(messages.ackReply(...)). AddedbridgeAckRemovingARealQueuedReplyReportsSuccessAndRemovesIt.InMemoryReplyInboxTest: boolean assertions onackRemovesTheMessage,ackForUnknownMsgIdIsNoOp,ackForUnknownTargetIsNoOp,peekAndAckAreNoOpsForUnownedTarget. Kept per review feedback: mutatingInMemoryReplyInbox.ack's unowned-target branch toreturn trueis killed by exactly these two of the four (ackForUnknownTargetIsNoOp,peekAndAckAreNoOpsForUnownedTarget) plusFleetMcpTest.bridgeAckOfACoordIdTargetIsAnErrorNamingFleetPoll— dropping them would leave that contract resting on one test.AmqpReplyInboxContractTest(new, added on review feedback):ackReportsHitVsMissAgainstARealBroker— amsgIdnever held for an owned target returnsfalsewithout throwing; a real held reply returnstrueand is removed; acking the samemsgIdagain returnsfalse. This is the one contract-group class CI actually runs, and it previously made zero assertions onack()'s return value, so the exact defect this ticket is about was unpinned in the adapter fleetd runs live. Verified against both broker modes the class supports: Testcontainers (AMQP_URIunset) and an external broker viaAMQP_URI(the CI shape — I used a disposablerabbitmq:3.13-managementcontainer on a throwaway port, not the shared local LavinMQ instance backing the live fleet, then stopped and removed it).Evidence
Full build, repo root (
fleetd/):mvn clean install→ Tests run: 1577, Failures: 0, Errors: 0, Skipped: 0 — BUILD SUCCESS. (The log has scatteredERROR/WARNlines from unrelated test fixtures simulating backend failures — e.g.OpenCodeLauncherTest,SessionManagerTest— not real failures; theTests run:line is the source of truth.)Control, default suite (unmutated):
mvn test -Dtest=FleetMcpTest,InMemoryReplyInboxTest→ Tests run: 101, Failures: 0 — BUILD SUCCESS.Control, contract suite (unmutated), both broker modes:
AMQP_URIunset):mvn -Pcontract test -Dtest=AmqpReplyInboxContractTest→ Tests run: 9, Failures: 0, Errors: 0, Skipped: 0 — BUILD SUCCESS.AMQP_URIset to a disposable container): same command → Tests run: 9, Failures: 0, Errors: 0, Skipped: 0 — BUILD SUCCESS.Mutation 1 (
FleetMcp.ackignoring the boolean, alwaysreturn text("acknowledged " + msgId); printed the mutated line to confirm it landed):mvn test -Dtest=FleetMcpTest→ Tests run: 85, Failures: 2, failing methods:dev.ltms.fleet.mcp.FleetMcpTest.bridgeAckOfAnIdInNoInboxIsAnErrordev.ltms.fleet.mcp.FleetMcpTest.bridgeAckOfACoordIdTargetIsAnErrorNamingFleetPollRestored, re-ran
mvn test -Dtest=FleetMcpTest,InMemoryReplyInboxTest→ Tests run: 101, Failures: 0 — BUILD SUCCESS.Mutation 2 (
AmqpReplyInbox.ack'sh == nullbranch changed toreturn true;— wasreturn false;; printed the mutated line to confirm it landed):mvn -Pcontract test -Dtest=AmqpReplyInboxContractTest(Testcontainers path) → Tests run: 9, Failures: 1, failing method:dev.ltms.fleet.msg.AmqpReplyInboxContractTest.ackReportsHitVsMissAgainstARealBroker—acking the same msgId twice must report false the second time ==> expected: <false> but was: <true>Restored, re-ran
mvn -Pcontract test -Dtest=AmqpReplyInboxContractTest→ Tests run: 9, Failures: 0 — BUILD SUCCESS. Then the fullmvn clean installabove (1577/0/0, BUILD SUCCESS) as the final state.Scope notes
AmqpReplyInboxContractTestwas named as the one CI actually runs.LeadChannel/LeadMailbox/LeadCoordLoop— confirmed (again) thatfleet_ackhas no route there and must not gain one.rabbitmq:3.13-managementcontainer I started and stopped myself, never against the shared local LavinMQ instance the live fleetd daemon uses, to avoid leaving orphaned durable queues on production infrastructure.ReplyInbox.ack now returns boolean (true = removed, false = nothing to remove) instead of void, so FleetMcp.ack can finally tell a hit from a miss. FleetMcp.ack returns an error when the boolean is false, naming fleet_poll{coordId} for held peer mail, which has no route through this call. MessageService.ackReply propagates the boolean; drainReplies keeps ignoring it (its own javadoc already documents that loss window as deliberate). Updated the tool schema's target description to match. Rewrote FleetMcpTest's ack tests to publish a real message before asserting success, and added tests for a never-queued id and a coord-id target, both now erroring. Added boolean assertions to InMemoryReplyInboxTest's existing ack cases.