Repository navigation
fix(task-bridge): dedup work() submissions with identical text within 2-minute window - #820
Conversation
… 2-minute window Closes sonichi#561. Voice over-delegation was causing Gemini to call work() 3-5 times with the same task text in rapid succession (sub-second gaps between calls). Each call wrote a separate task file, triggering parallel executions and a cascade of duplicate result DMs. Fix: before writing the task file, scan _pendingTasks for any entry whose normalised text matches the incoming task and was submitted within the last 2 minutes. On a match, return status=duplicate with the existing taskId instead of creating a new file. The voice agent's instruction text tells Gemini to report "already working on it" rather than queuing again. normalizeTask() lowercases, collapses whitespace, and caps at 150 chars so minor phrasing variations ("check X" vs "Check X") still dedup, but genuinely different tasks with overlapping prefixes do not collide.
qingyun-wu
left a comment
There was a problem hiding this comment.
Cold review — LGTM. Closes a real failure mode I've seen in build_log entries (voice over-delegating "对就是说对" as 3 tasks in a 5-second window).
Verified
normalizeTaskis appropriately fuzzy: lowercase + whitespace-collapse + trim + slice(0, 150). Catches "send Bob an email" vs "Send Bob an email." vs "Send Bob an email" — same task.DEDUP_WINDOW_MS = 2 minis well-sized. Voice over-delegation typically happens in <10 seconds; legitimate "I want to send Bob another email" wouldn't come back within 2 min of an identical phrasing.- The duplicate-return shape (
{status, taskId, message}) tells the voice agent to NOT re-issue and what taskId is already in flight — perfect for the LLM's instruction-following. - The message string ends with "Do NOT submit again — tell the user you are already working on it" — uses the LLM-coercion pattern that's worked well in other tool descriptions.
Non-blocking observations
-
Single-user assumption. Dedup is global across all callers. In a multi-tier setup (owner + team-tier callers), two different users submitting the same task text would collapse to one. Probably fine — same text → same intended action. If it bites, add
senderIdto the dedup key. -
150-char truncation. Two different long tasks that happen to share the first 150 chars (e.g., "Read the paper at /path/A.pdf and summarize" vs "Read the paper at /path/B.pdf and summarize" — if the path makes the difference past 150 chars) would dedup wrongly. Unlikely in practice but worth a comment that the truncation is intentional bounded-cost.
-
Memory growth.
_pendingTasksis cleared on task completion (per the rest of the file's invariants, presumably). The dedup window is 2 min, which caps the worst-case map size. Probably fine — but worth a comment that the dedup behavior depends on cleanup happening correctly elsewhere. -
status: 'duplicate'is a new return shape. Confirm no caller depends on always seeingstatus: 'pending'or similar — a quick grep forworkToolreturns.
Ship it.
sonichi
left a comment
There was a problem hiding this comment.
LGTM. Correct fix for the known voice over-delegation pattern (PR description nails the symptom). _pendingTasks cleanup at the 5 .delete sites prevents unbounded growth. Normalization (lowercase + collapse-whitespace + 150-char prefix) is reasonable. Ship.
… 2-minute window (sonichi#820) Closes sonichi#561. Voice over-delegation was causing Gemini to call work() 3-5 times with the same task text in rapid succession (sub-second gaps between calls). Each call wrote a separate task file, triggering parallel executions and a cascade of duplicate result DMs. Fix: before writing the task file, scan _pendingTasks for any entry whose normalised text matches the incoming task and was submitted within the last 2 minutes. On a match, return status=duplicate with the existing taskId instead of creating a new file. The voice agent's instruction text tells Gemini to report "already working on it" rather than queuing again. normalizeTask() lowercases, collapses whitespace, and caps at 150 chars so minor phrasing variations ("check X" vs "Check X") still dedup, but genuinely different tasks with overlapping prefixes do not collide. Co-authored-by: Chi Wang <4250911+sonichi@users.noreply.github.com>
Summary
workTool.execute()insrc/task-bridge.tsstatus: 'duplicate'with the existingtaskIdinstead of writing a second task filePendingTasktype withtaskText: stringto enable the checknormalizeTask()lowercases + collapses whitespace + truncates to 150 chars to absorb minor phrasing variations without false-positiving on genuinely distinct tasksCloses #561.
Root cause
Voice over-delegation: Gemini calls
work()3-5 times in rapid succession with identical (or near-identical) task text. Each call wrote a separate task file, triggering parallel executions and a cascade of duplicate result DMs to the owner.Test plan
work("check my calendar")call →status: pending, task file writtenwork("check my calendar")call within 2 min →status: duplicate, no second filework("check my calendar")call after 2 min → new task file written normallywork("Check my calendar")(different casing/spacing) → deduped against originalnpx tsc --noEmitpasses🤖 Generated with Claude Code