Skip to content

[P1] Preserve line-start A2A handoffs when the target already has active work #1335

Description

@mindfn

Problem

A valid line-start @target can be persisted but never invoke its target when that cat already has an active or queued agent entry. This drops an ordinary A2A handoff rather than retaining it until the target slot is free.

Observed in thread thread_mruayc4owlyzazbx:

  • Fable posted a valid line-start @sol with the Q3/Q4 handoff (0001786435553718-000283-0311fbcd).
  • Sol was completing an earlier turn in the same timing window; the first response did not consume Q3/Q4.
  • A later manual @sol (0001786436054020-000300-40672525) did invoke Sol, which then answered Q3/Q4. This rules out handle parsing, availability, and provider failure.

Root cause

resolveInlineCat() returns skip: dedup_active whenever hasActiveAgent(cat) is true. routeSerial() then immediately continues, so the ordinary text-derived handoff never reaches claimOrDeferA2ATarget(), the lower path that is designed to durably enqueue an occupied target.

Relevant paths:

  • packages/api/src/domains/cats/services/agents/routing/routing-decision.ts
  • packages/api/src/domains/cats/services/agents/routing/route-serial.ts
  • packages/api/src/domains/cats/services/agents/invocation/QueueProcessor.ts

The present unit test explicitly locks in the losing behavior: routing-decision.test.js expects skip: dedup_active for a processing target.

Expected behavior

A busy target must prevent a concurrent invocation, not erase the handoff. Preserve one durable A2A carrier using the original trigger message as a2aTriggerMessageId; when the active slot is released, invoke the target exactly once. Repeated identical handoffs may coalesce, but must remain observable and must not silently disappear.

Acceptance criteria

  • A line-start mention to a target with an active/queued agent entry produces a durable queued/coalesced A2A carrier instead of skip: dedup_active.
  • The carrier retains the originating message ID and target, survives target-slot release, and invokes the target exactly once.
  • No concurrent duplicate target invocation is started.
  • A regression test exercises the real queue-backed route path (not only routeSerial with the active-queue hook omitted).
  • F167 custody/diagnostic projections observe a real queued handoff rather than a text-only false success.

Relation to #1312

Related to #1312 (F254 message delivery attempts), but not part of its scope. #1312 begins after a durable queue carrier exists and provides per-target failure/retry custody. This defect occurs before carrier admission, so there is nothing for #1312 to retry. Keep the fixes separate; both may share queue/custody primitives after the admission contract is corrected.

Metadata

Metadata

Assignees

No one assigned

    Labels

    acceptedMaintainer accepted: ready for implementation/mergebugSomething isn't workingtriagedMaintainer reviewed, replied, and made an initial triage decision

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions