Skip to content

[Team] Recover task handoffs without duplicate work #55

Open

Description

@hsliuustc0106

Planning ID: TODO-J

Proposed issue / PR title: fix(tasks): recover acknowledged handoffs and deduplicate retries.

Goal: preserve the same task across disconnects and process interruptions.

Scope: durable handoff acknowledgements, delivery keys, per-task retry/reconciliation and state persistence around delivery; integrate existing resume behavior. Expose uncertain side-effect outcomes for reconciliation rather than blindly rerunning. Recheck membership, role and approvals after recovery.

Out of scope: claiming exactly-once execution for external services that do not support it; distributed consensus infrastructure.

Dependencies: TODO-I (#54).

Code area: task persistence, delivery/exchanges, resume, client reconnect/history reconciliation.

Acceptance and tests: inject interruptions before/after persisted send, acknowledgement, execution and result receipt; replay delivers one logical handoff/result and retains correct task ownership. External actions with uncertain completion pause for reconciliation. Cancellation/revocation survives restart, and both clients reconnect to consistent status.

Plan update (2026-10-16, team mode via groups): No separate team-mode subsystem is needed; groups with per-Bot grants cover it, and this is step 3 of the ordered plan on #51 (after step 1 #149 and step 2). Effort: L.

  • Gives: exchangeId is stable across resume, retry and resend; inbound seen set and cursors dedupe; staleReason / ownerPolicy recheck on release; inflight.json records queued deliveries.
  • Gap: no durable per-task delivery key or ack; outbound send_as_member is not idempotent (a crash after send and before save can duplicate); a task cannot say a side effect is uncertain; resumeTurn does not know the task.
  • Per-task handoff key: task.handoff = {key: taskId:seq, sentAt, ackedAt}, stamped as meta.handoffKey in hooks.js and stored on the exchange. In plugins/social/src/social.js sendOut, record the key before the call and the message id after.
  • Receiver dedup: the receiver drops a repeated handoffKey (extend markSeen / rt.deliver).
  • uncertain state: a restart that finds a pending key and no message id marks the task uncertain and asks the owner to reconcile with a card, never a blind resend.
  • Resume re-checks: resume.js looks up taskId from the trigger and re-runs mayCreateOn and the grant/membership check before resuming. Persist cancellation in state.tasks; reconnect reads tasks.
  • Files: tasks.js, hooks.js, resume.js, delivery.js, plugins/social/src/social.js.

Delivery

One focused implementation PR for this issue. This is planned work, not a claim of shipped multi-human support. Preserve the pristine upstream pin and generated patch/client boundaries. Keep team mode disabled until the documented authorization gates pass. Record exact tested head, executed checks, missing checks and independent review. No production credentials or live deployment are part of this issue.

Activity

  1. added
    needs-humanThe agent loop stopped; a person must decide (reason in the last comment)
    on Oct 20, 2026
  2. Birfy commented on Oct 20, 2026

    @Birfy
    Collaborator

    Triaged: this is team-via-groups work, and the team mode contract is not shipped (AGENTS.md: team mode stays disabled until the authorization gates in docs/subsystems/team.md pass; issue #16 is resolved as Option B and must not be re-opened).

    So it is not agent-ready: it waits for the owner to explicitly turn team work on. Labeled needs-human; automation leaves it alone and writes no code for it.

  3. added a commit that references this issue on Oct 27, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    area:automationScheduled tasks and automationenhancementNew feature or requestneeds-humanThe agent loop stopped; a person must decide (reason in the last comment)

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions