Skip to content

Redis Streams Contract

This page DOCUMENTS the stream topology. It is NOT the source of truth.

AUTHORITATIVE SOURCE: config/dolly-routing.yaml (for stream names and routing rules) AUTHORITATIVE SOURCE: config/ports.yaml (for Redis connection details)

Code that uses a stream name MUST read it from config/dolly-routing.yaml or use the constant defined in the executor code. Code MUST NOT hardcode stream names as string literals.

Connection

Host: redis.ge-system.svc.cluster.local (k8s service DNS) Port: DEFINED IN config/ports.yaml (currently 6381 as of 2026-02-14) Password: from Vault at ge/redis StatefulSet: redis-0 in ge-system namespace

Stream Topology (as documented in INFRA-OVERVIEW 2026-02-14)

Agent Triggers

Pattern: triggers.{team}.{agent_name} (team-bound agents) or triggers.{agent_name} (shared agents) Consumer group: executor-group One stream per registered agent per team (team-bound agents get per-team streams; shared agents keep a single stream) Authoritative list of agents: ge-ops/master/AGENT-REGISTRY.json

Work Completion

Stream: ge:work:completed Published by: PLANNED (not yet implemented) — executor currently writes COMP-*.md files, host cron syncs to DB. ge-orchestrator listens on this stream but nothing publishes to it yet.

System

Stream: system.halt -- HALT broadcasts (all agents subscribe) Stream: system.health -- Health check heartbeats

Notifications

Stream: notifications.queue -- Portal notification routing

Decisions

Stream: decisions.pending -- Human decisions awaiting response Stream: decisions.resolved -- Resolved decisions

Triad Coordination (ge:triad:*)

System-level coordination streams for the autonomous reviewer↔executor↔DJ cycle. Not agent-trigger streams — registered under coordination_streams: in config/dolly-routing.yaml (not as per-agent redis_stream:).

Stream Direction MAXLEN Consumer group
ge:triad:to-executor reviewer/DJ → executor 500 triad-executor-group
ge:triad:to-reviewer executor/DJ → reviewer 500 triad-reviewer-group
ge:triad:to-dj reviewer → DJ inbox 100 triad-dj-group
  • MAXLEN is a per-stream cap (≤1000 for system-level streams) per DEC-20260529-A — 500/500/100 is conformant; the sum (1100) is NOT an aggregate violation.
  • Bodies > 2KB spill to sidecar files (/home/claude/triad-messages/<correlation_id>.md); the stream carries a content_path pointer.
  • Full message schema + polling + DJ-inbox design: ge-ops/wiki/docs/development/roadmaps/triad-broker-v1-design.md (§4 streams, §5 schema, §6 polling).
  • Created by scripts/triad/setup-streams.sh (idempotent; XGROUP CREATE BUSYGROUP treated as no-op).

Message Format (Agent Triggers)

Reference format from INFRA-OVERVIEW. Verify against actual executor code at ge_agent/runner.py:

{
  "trigger_id": "trig-{ISO-date}-{sequence}",
  "timestamp": "{ISO-8601}",
  "work_item": "{path to work item or task reference}",
  "context": {
    "client": "{client-slug}",
    "project": "{project-slug}",
    "team": "alpha|beta"
  },
  "triggered_by": "{agent_name|system|TaskService}",
  "reason": "{trigger_reason}"
}

Consumer Group Configuration

The shared executor uses Redis consumer groups to ensure each trigger is delivered to exactly one pod:

  • Group name: executor-group (all executor pods share this group)
  • Consumer name: $POD_NAME (unique per pod, from k8s downward API)
  • Read mode: XREADGROUP with > (new messages only for each consumer)
  • ACK: after execution completes (success or failure)
  • Orphan recovery: on startup, each pod claims and ACKs messages from dead consumers (idle > 60s) to prevent pending message buildup from pod restarts
  • Dedup: exec_dedup:{project_id}:{work_item_id} Redis key with 5-min TTL prevents double execution even if the same trigger is delivered twice

Source of truth: ge_agent/listener.py

Rules

  • ALL XADD calls MUST include MAXLEN. Cap is per-stream (per DEC-20260529-A): ~100 for agent-scoped (triggers.*) streams; ≤1000 for system-level (ge:platform:*, ge:triad:*) streams. NOT an aggregate budget.
  • New streams require registration in config/dolly-routing.yaml BEFORE code is written (agent-trigger streams as per-agent redis_stream:; non-agent system streams under coordination_streams:)
  • Dedup on work_item_id with 5-min window prevents double execution
  • Max hook chain depth: 3 (tasks that create tasks that create tasks)
  • Hook depth: 1 for no_block tier
  • Monitoring agent isolation: see hook-loops pitfall