Repository navigation
[Team] Recover task handoffs without duplicate work #55
Description
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:
exchangeIdis stable across resume, retry and resend; inboundseenset and cursors dedupe;staleReason/ownerPolicyrecheck on release;inflight.jsonrecords queued deliveries. - Gap: no durable per-task delivery key or ack; outbound
send_as_memberis not idempotent (a crash after send and before save can duplicate); a task cannot say a side effect is uncertain;resumeTurndoes not know the task. - Per-task handoff key:
task.handoff = {key: taskId:seq, sentAt, ackedAt}, stamped asmeta.handoffKeyinhooks.jsand stored on the exchange. Inplugins/social/src/social.js sendOut, record the key before the call and the message id after. - Receiver dedup: the receiver drops a repeated
handoffKey(extendmarkSeen/rt.deliver). uncertainstate: a restart that finds a pending key and no message id marks the taskuncertainand asks the owner to reconcile with a card, never a blind resend.- Resume re-checks:
resume.jslooks uptaskIdfrom the trigger and re-runsmayCreateOnand the grant/membership check before resuming. Persist cancellation instate.tasks; reconnect readstasks. - 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
Agent inbox: show failed turns and a latest-activity line on each card (
Birfy commentedon Oct 20, 2026
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.mdpass; 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. Labeledneeds-human; automation leaves it alone and writes no code for it.fix(issue-ref): read a bare ThinkFlowLab#1234 only when the number st…