Skip to content

fix(task-bridge): dedup work() submissions with identical text within 2-minute window - #820

Merged
sonichi merged 2 commits into
sonichi:mainfrom
jayjaybeeuk:fix/task-bridge-dedup-work-submissions
May 18, 2026
Merged

sonichi merged 2 commits into
sonichi:mainfrom
jayjaybeeuk:fix/task-bridge-dedup-work-submissions

Conversation

@jayjaybeeuk

Copy link
Copy Markdown
Contributor

Summary

  • Adds deduplication to workTool.execute() in src/task-bridge.ts
  • If the same task text arrives again within 2 minutes (voice over-delegation pattern), returns status: 'duplicate' with the existing taskId instead of writing a second task file
  • Extends PendingTask type with taskText: string to enable the check
  • normalizeTask() lowercases + collapses whitespace + truncates to 150 chars to absorb minor phrasing variations without false-positiving on genuinely distinct tasks

Closes #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

  • Single work("check my calendar") call → status: pending, task file written
  • Second work("check my calendar") call within 2 min → status: duplicate, no second file
  • Second work("check my calendar") call after 2 min → new task file written normally
  • work("Check my calendar") (different casing/spacing) → deduped against original
  • Two different tasks with similar 150-char prefix but different text → both accepted
  • TypeScript: npx tsc --noEmit passes

🤖 Generated with Claude Code

… 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.
@CLAassistant

CLAassistant commented May 18, 2026 •

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@qingyun-wu qingyun-wu left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

  • normalizeTask is 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 min is 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

  1. 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 senderId to the dedup key.

  2. 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.

  3. Memory growth. _pendingTasks is 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.

  4. status: 'duplicate' is a new return shape. Confirm no caller depends on always seeing status: 'pending' or similar — a quick grep for workTool returns.

Ship it.

@sonichi sonichi left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

@sonichi
sonichi merged commit 792d38b into sonichi:main May 18, 2026
2 checks passed
liususan091219 pushed a commit to liususan091219/sutando-fork that referenced this pull request May 28, 2026
… 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>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

task-bridge: dedup duplicate work() submissions while a same-text task is pending

4 participants