IBEX Harness
DocsBenchmarksBlogChangelogRoadmap
GitHub
IBEX Harness

Documentation

Architecture Decision RecordsADR-0002: Repository foundation bootstrapADR-0003: Branch protection and merge policyADR-0004: Protobuf and code generation policyADR-0005: Postgres migration strategyADR-0006: Auth protobuf contract (`ibex.auth.v1`)ADR-0007: Auth token validation implementationADR-0008: Security scanning and CI quality gatesADR-0009: Permission bitmap layoutADR-0010: Cryptography policyADR-0011: Proxy auth gRPC client and middlewareADR-0012: Proxy request normalization (OpenAI chat)ADR-0013: Proxy input validation and stable error envelopeADR-0014: Core domain migration sequencingADR-0015: Proxy rate limit skeleton (Phase 1)ADR-0016: Proxy agent identity verification (Phase 1)ADR-0017: Request ID and trace context strategy (Phase 1)ADR-0018: Graceful shutdown contract (Phase 1)ADR-0019: OpenTelemetry provider configuration (Phase 1)ADR-0020: Shared package boundaries — `packages/config` and `packages/apierror`ADR-0021: Prometheus Metric Catalog (Phase 1)ADR-0022: Health check contract (Phase 1)ADR-0023: Docs site architecture (Phase 1.5)ADR-0024: Benchmark data publishing modelADR-0025: LLM provider abstractionADR-0026: OpenAI client designADR-0027: Streaming dual-write strategyADR-0028: Auth cache designADR-0029: Token revocation propagation via Redis pub/subADR-0030: Directive versioning strategyADR-0031: System prompt injection strategyADR-0032: Session data model and retentionADR-0033: ClickHouse llm_traces schema and retentionADR-0034: Proxy overhead performance measurement methodologyADR-0035: Chat Idempotency-Key Redis dedupeADR-0038: Context assembly service design and gRPC contractADR-0039: Proxy Postgres ownership for session and directive storesADR-0040: Anthropic provider adapterADR-0041: Model capability registryADR-0042: Self-hosted OpenAI-compatible LLM adapterADR-0043: Tokenizer registry architectureADR-0044: Non-streaming response pipelineADR-0045: Streaming response transformationADR-0046: Embedder interface and profile registryADR-0047: Memory temporal validity foundationADR-0048: Memory multi-label categoriesADR-0049: Memory relationship graph readinessADR-0050: MCP server skeleton (transport, auth, audit)ADR-0051: Local LGTM observability stack (Phase 2.5 exit pull-forward)ADR-0052: Memory schema v2 expand (HNSW, quality columns)ADR-0053: Vector store abstraction and composite scoring v2ADR-0054: In-process Presidio PII detection in memoryADR-0055: Memory write-path exact and near-duplicate dedupADR-0056: Temporal-interval-aware conflict detectionADR-0057: Memory write-path persistence and orchestrationADR-0058: Semantic search FTS fallback triggerADR-0059: Hot cache read pathADR-0060: Memory service tenant isolation architectureADR-0061: Retrieval-quality benchmark methodologyADR-0062: Worker task observability and dead-letter handlingADR-0063: Extraction output schema v2ADR-0064: Extraction provider registry and batch executionADR-0065: Extraction batch idempotency and turn pointer orderingADR-0066: Extraction quality evaluation harnessADR-0067: Context capability catalog generate-and-diff for token budgetsADR-0068: Composite scoring-time relevance gateADR-0069: Context packer bounded DP knapsackADR-0070: Context formatting locked ordering and session nonceADR-0071: Context assembly gRPC degradation ladder and retrieval deadlineADR-0072: Extraction enqueue via worker HTTP (not Celery Redis wire format)ADR-0073: Organization Lifecycle and Cascading SuspensionADR-0074 Provider Credential StorageADR-0075 Per-Org Model Routing PolicyADR-0076: Circuit Breaker per ProviderADR-0077: Provider Fallback RoutingADR-0078: Operator runtime topology & environment contract (4.P.0)ADR-0079: Operator session RS256 dual-verify and TOTP step-upIBEX Console service rename and boundaryADR-0081: Fail-closed Proxy runtime controlsD2 metadata-only trace read contractD2 publication, identity, query, and lifecycle contract
ADRs›ADR-0072: Extraction enqueue via worker HTTP (not Celery Redis wire format)
ADRs

ADR-0072: Extraction enqueue via worker HTTP (not Celery Redis wire format)

Architecture decision record 0072 — proxy terminates sticky external_id sessions, buffers turns in Redis, and enqueues extract_session_memories through an internal Starlette endpoint on the worker that calls Celery apply_async.

ADR-0072: Extraction enqueue via worker HTTP (not Celery Redis wire format)

  • Status: Accepted
  • Date: 2026-09-06
  • Authors: IBEX Harness team
  • Milestone: 3.5.D.4 Extraction task enqueue
  • Depends on: ADR-0064, ADR-0065, ADR-0032

Context

Milestone 3.5.D.4 originally sketched a Go proxy that LPUSHes Celery protocol messages onto Redis broker lists. Discovery against live main found:

  1. Nothing production-set sessions.status = 'completed' — only idle abandon → abandoned.
  2. Checkpoints store hashes only; turn text for extract_session_memories must be supplied in the Celery kwargs (ADR-0064).
  3. Celery/kombu Redis transport uses priority-separated list keys and protocol v2 envelopes (base64 body of [args, kwargs, embed]). A subtly wrong Go envelope enqueues successfully but is never consumed — worse than an HTTP error.

The worker already imports Celery. The proxy must not.

Decision

1) Internal HTTP enqueue on the worker

Expose POST /internal/extraction/enqueue on a small Starlette app co-located with the Celery worker process (own port, default 8007). The handler validates the payload and calls extract_session_memories.apply_async(kwargs=...). The proxy is an HTTP client only.

2) Static Bearer auth (embedder pattern)

Authenticate proxy→worker with IBEX_WORKER_ENQUEUE_API_TOKEN and hmac.compare_digest, matching embedder’s IBEX_EMBEDDING_API_TOKEN pattern. Do not reuse the worker→memory org-scoped PAT trust boundary.

3) Session terminate triggers enqueue on first completion and on recovery

POST /v1/sessions/{session_id}/terminate uses sticky external_id (same value as X-IBEX-Session-ID), scoped by X-IBEX-Agent-ID. Store.CompleteByExternalID returns a typed result:

  • CompleteOK (first transition to completed): peek the Redis turn buffer and enqueue when turns are present.
  • CompleteNoop (already completed): may still enqueue if and only if the buffer still holds unacknowledged turns — that means a prior enqueue attempt never completed (fail-open retention). This is deliberate recovery, not accidental re-work.
  • After a successful Ack: the peeked snapshot is deleted; a later Noop terminate Peeks empty → skip enqueue (log + metric). Never fabricate turns.

Duplicate apply_async submissions (races between concurrent terminate calls, or Noop recovery after a delayed original enqueue already succeeded) are not prevented solely by the proxy. They are bounded by:

  1. the worker enqueue surface’s in-process Idempotency-Key cache (TTL; keyed by session id), and
  2. durable consumer identity idempotency in ADR-0065 at the memory-write layer.

4) Transient Redis turn buffer

Proxy appends turn role/content to Redis after successful chat completions ({org_id}:session:{agent}:{external_id}:extraction_turns, TTL-bound) synchronously before the chat handler returns (checkpoint/trace stay async). Terminate peeks the buffer to populate turns and deletes only after the worker accepts enqueue (compare-and-delete Ack). Empty/expired buffer → skip enqueue (log + metric); never fabricate turns. Buffer write failures are fail-open on the chat path.

5) Non-goals

  • Not a general-purpose Celery task API (this one task shape only).
  • No abandoned-session extraction triggering.
  • No long-term turn archival (still missing per ADR-0064 / ADR-0032).
  • No hand-rolled Celery Redis wire protocol in Go.

Consequences

  • Milestone 3.5.D.4 success signals drop “raw LPUSH / Celery wire format” and require HTTP enqueue + terminate + turn buffer.
  • Operators must configure enqueue base URL + shared token on the proxy and worker.
  • Steady-state “no duplicate work after success” is Peek empty after Ack (proxy) plus worker Idempotency-Key (process-local TTL) plus ADR-0065 durable consumer identity — not “enqueue never runs on Noop.”

Alternatives considered

  1. Hand-rolled Go LPUSH — rejected due to silent consumption failure risk (priority keys + protocol v2).
  2. Separate worker-api compose service — deferred; co-located Starlette on the worker process is enough for this milestone.
  3. Enqueue on abandoned — out of scope; worker still requires status=completed.

Was this page helpful?

Edit on GitHub

Last updated on

PreviousADR-0071: Context assembly gRPC degradation ladder and retrieval deadlineNextADR-0073: Organization Lifecycle and Cascading Suspension

On this page

  • Context
  • Decision
  • 1) Internal HTTP enqueue on the worker
  • 2) Static Bearer auth (embedder pattern)
  • 3) Session terminate triggers enqueue on first completion and on recovery
  • 4) Transient Redis turn buffer
  • 5) Non-goals
  • Consequences
  • Alternatives considered
0%