Intelligence

Workers

Celery queues, task lifecycle, dead letters, enqueue admission, and operational limits.

The worker is a Python Celery process with four logical queues. It is not a single monolith task: each family has a different durability boundary.

Provisional Ledger class: provisional
Verified 2026-10-21Task families are implemented; queue durability and exactly-once projection are not certified

Queues and order

celery -A app.celery_app:celery_app worker \
  -Q extraction,embedding,maintenance,mcp_audit \
  --loglevel=info

Cross-queue order is the -Q order. Intra-extraction priority uses Celery Redis priority steps 0–9. RabbitMQ x-max-priority is not used.

Task families

FamilyResponsibilityStatus
ExtractionRead completed session turns, call an extraction provider, write memoriesImplemented; live vLLM manual
EmbeddingAsync embedding jobsImplemented
Organization deletionOrchestrate tenant deletion and receiptsImplemented; certification open
BillingReconcile usage and roll up budgetsImplemented; hosted evidence open
MaintenanceBeat sweep and housekeepingNo-op/deferred
MCP auditFan out tool-call audit eventsImplemented; sink durability varies

Retry and dead letters

IbexTask owns the retry policy. There is no per-task on_failure override; lifecycle signals in app/observability.py handle failures.

Exhausted retries are persisted to ibex_core.failed_tasks and increment ibex_worker_task_dead_letter_total{task_name}. The alert rule is IBEXWorkerTaskDeadLettered.

A dead-letter row is evidence that retries were exhausted. It is not automatic remediation. Operators must inspect the task, decide whether replay is safe, and verify the downstream effect.

Enqueue admission

POST :8007/internal/extraction/enqueue accepts extraction work only when IBEX_WORKER_ENQUEUE_API_TOKEN is set. The endpoint requires that static bearer plus an Idempotency-Key of at most 256 characters.

Admission behavior:

  • The normalized payload, including every turn, is hashed.
  • The tenant-scoped PostgreSQL idempotency ledger claims the key before Celery dispatch.
  • Replay returns the original task ID.
  • A changed payload returns 409.
  • An active claim returns 409 with Retry-After.
  • Dispatch failure releases the claim.
  • If dispatch succeeds but finalization does not commit, the endpoint returns 503; retries may see 409 until the 120-second lease expires.
  • Without PostgreSQL, the endpoint fails closed with 503.

GET :8007/health is unauthenticated liveness only. GET :8007/ready checks the token, PostgreSQL ledger, and Redis broker.

Configuration highlights

VariableDefaultWhy it matters
IBEX_WORKER_BROKER_URLderivedFull broker override
IBEX_WORKER_RESULT_BACKENDderivedFull result-backend override
REDIS_DB_QUEUE1Keeps queue keys out of cache DB
REDIS_DB_RESULTS3Result TTL storage
IBEX_WORKER_ENQUEUE_API_TOKENunsetMissing means the enqueue server does not start
IBEX_WORKER_METRICS_PORT8006Prometheus exposition
IBEX_WORKER_ENQUEUE_PORT8007Enqueue and readiness HTTP
IBEX_WORKER_EXTRACTION_PROVIDERopenaiopenai or vllm
IBEX_WORKER_MEMORY_BASE_URLunsetMemory service origin
IBEX_WORKER_MEMORY_API_TOKENunsetBearer with memory:write
OTEL_SAMPLE_RATIO0.01Trace sampling

Run locally

make compose-dev-up
make db-migrate
cd services/worker && uv sync --extra dev

export POSTGRES_DSN='postgres://ibex:[email protected]:5432/ibex?sslmode=disable'
celery -A app.celery_app:celery_app worker \
  -Q extraction,embedding,maintenance,mcp_audit \
  --loglevel=info

Use make worker-beat-dev for beat and make worker-ping for a broker health check.

Limits

  • Queue registration is not a production-readiness claim.
  • Result TTL and task_ignore_result defaults matter for retry visibility.
  • The maintenance sweep is intentionally a no-op.
  • There is no GPU CI runner; mock transports do not prove live vLLM behavior.
  • Exactly-once projection into PostgreSQL, ClickHouse, and caches is not claimed.