Skip to content

broker: idempotent POST /tasks returns existing record indistinguishably from a fresh create (no flag, no audit) #2010

Description

@seoseo-ai

Summary

POST /tasks on the classic path is idempotent by client-supplied id: when a task with the requested id already exists, createTask silently returns the existing record (broker.ts "Idempotent create" block, unchanged through 674fc75b). The HTTP response for an idempotent return is byte-indistinguishable from a fresh create — no flag, no status distinction, no audit event.

This caused a real operational incident during the skills-intake review gate (context: #2007): a fleet dispatcher reused a fixed task id across 6 dispatch rounds. Rounds 2–6 were silent no-ops (idempotent returns of the round-1 record), but the dispatcher counted each response as created=1 and archived the same round-1 result 6 times as if they were per-round results. A false "broker rewrites assignedWorkerId at create" bug hypothesis was investigated for hours before the audit log showed only one task.created ever happened.

Problems

  1. Caller cannot distinguish "created" from "returned existing". The idempotent return is a deliberate design (good for retries), but it is unobservable.
  2. No audit trail for idempotent hits. A create attempt against an existing id leaves zero broker-side evidence (no task.created, no deny), which made incident reconstruction rely on client-side logs.
  3. Inconsistent semantics with the live-task path. handleCreateTaskRequest rejects duplicate ids for payload.mode === live tasks with invalid_transition ("task X already exists"), while the classic path silently returns the old record.

Suggested fix (any subset)

  • Include an explicit marker on the response for idempotent returns (e.g., idempotentReturn: true, or created: false), and/or
  • Emit an audit event (e.g., task.create_idempotent_hit) with the requested vs existing id, and/or
  • Document the idempotent contract in the tasks API spec so dispatchers know to treat an existing-id response as "already exists" rather than "created".

Happy to follow up with a PR if the maintainer picks a direction.

Activity

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

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions