Skip to content

bug: operator_queue.id is a global primary key — cross-agent id collision silently swallows requests #1631

Description

@vybe

Summary

operator_queue.id is a TEXT PRIMARY KEY shared fleet-wide; the create path uses on_conflict_do_nothing(index_elements=["id"]) (src/backend/db/operator_queue.py) and the 5s sync loop's dedup is id-only (operator_queue_item_exists(req_id), no agent scoping). Two agents choosing the same request id (e.g. the old date-serial example req-20260307-001) means the second agent's request is silently never created — never shown to an operator, never responded to, and never flipped terminal in the agent's file (write-back queries are agent-scoped), so the agent waits forever with zero log signal.

Context

Found in the #1402 review. The #1402 contract mitigates by mandating execution-id-derived request ids (approval-{execution_id}-{slug}) in the platform prompt and agent guide, but the schema-level fix is scoping uniqueness to the agent. A related lower-severity wrinkle: the lease reaper's platform-created poison-{execution_id} ids share the same global namespace, so an adversarial agent can pre-create a matching id to replace the platform's alert text with its own (_create_park_item still returns True on conflict, so the row still parks — visibility is degraded, not lost).

Acceptance Criteria

  • Queue-item uniqueness is scoped to (agent_name, id) (dual-track migration: SQLite db/migrations.py + Alembic revision + db/schema.py + db/tables.py), OR — if the PK migration is judged too disruptive — a cross-agent conflict on create is detected and logged loudly (WARN + audit) instead of silently swallowed.
  • Sync-loop dedup (operator_queue_item_exists) checks the same scoped key.
  • Idempotent re-insert of the SAME agent's same id keeps today's on-conflict no-op behavior (the feat: MAX_REDELIVERY cap + async operator-queue human-gate lever (lease-reaper backstop) #1402 re-park dedup relies on it; the reaper's alert-first idempotency too).
  • Regression test: two agents writing the same id both get visible queue items (or the loud-conflict path fires).

Technical Notes

  • src/backend/db/operator_queue.py (create/dedup), src/backend/services/operator_queue_service.py (sync), services/lease_reaper_service.py (poison-{eid} items).
  • Migration reminder: schema changes touch FOUR files (schema.py, tables.py, migrations.py, Alembic revision) — see docs/memory/learnings.md 2026-06-23.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions