Files
community-ade/docs/redis-schema.md
Ani (Annie Tunturi) ce8dd84840 feat: Add approval system and agent config UI
- Omega (Kimi-K2.5): Approval system architecture
  - design.md: Full system architecture with state machines
  - api-spec.ts: Express routes + Zod schemas (33KB)
  - redis-schema.md: Redis key patterns (19KB)
  - ui-components.md: Dashboard UI specs (31KB)

- Epsilon (Nemotron-3-super): Agent configuration UI
  - AgentWizard: 5-step creation flow
  - AgentConfigPanel: Parameter tuning
  - AgentCard: Health monitoring
  - AgentList: List/grid views
  - hooks/useAgents.ts: WebSocket integration
  - types/agent.ts: TypeScript definitions

Total: 150KB new code, 22 components

👾 Generated with [Letta Code](https://letta.com)
2026-03-18 12:23:59 -04:00

19 KiB

Community ADE Approval System - Redis Schema

Overview

This document defines all Redis key patterns used by the Approval System. All keys use the ade: prefix for namespacing.


Key Naming Convention

ade:{category}:{subcategory}:{identifier}:{attribute}
Segment Description Examples
ade Global namespace prefix -
category High-level component lock, approval, task, session
subcategory Specific entity type task, resource, agent, user
identifier Unique entity ID UUID or slug
attribute Property/attribute data, queue, index

Lock Keys

Primary Lock Storage

Task Lock (Exclusive)

Key: ade:lock:task:{task_id}
Type: Hash
TTL: 30 seconds (renewable)

Fields:
  holder_agent_id    (string)    UUID of agent holding the lock
  acquired_at        (string)    ISO 8601 timestamp
  expires_at         (string)    ISO 8601 timestamp
  purpose            (string)    Human-readable purpose
  heartbeat_count    (integer)   Number of heartbeats received
  queue_length       (integer)   Number of waiters in queue

Example:
  HSET ade:lock:task:550e8400-e29b-41d4-a716-446655440000 \
    holder_agent_id agent-123 \
    acquired_at "2026-03-18T15:30:00Z" \
    expires_at "2026-03-18T15:30:30Z" \
    purpose "Applying database migration"

Resource Lock (Shared/Exclusive)

Key: ade:lock:resource:{resource_type}:{resource_id}
Type: Hash
TTL: 30 seconds (renewable)

Fields:
  mode               (string)    "exclusive" or "shared"
  holders            (JSON)      Array of {agent_id, acquired_at}
  exclusive_holder   (string)    Agent ID (if exclusive mode)
  acquired_at        (string)    ISO 8601 timestamp
  expires_at         (string)    ISO 8601 timestamp

Example:
  HSET ade:lock:resource:database:prod-db-01 \
    mode "exclusive" \
    exclusive_holder agent-456 \
    acquired_at "2026-03-18T15:30:00Z" \
    expires_at "2026-03-18T15:30:30Z"

Agent Capacity Lock

Key: ade:lock:agent:{agent_id}:capacity
Type: Hash
TTL: None (persistent, cleaned up on agent deregistration)

Fields:
  max_tasks          (integer)   Maximum concurrent tasks
  active_tasks       (integer)   Currently executing tasks
  queued_tasks       (integer)   Tasks waiting for capacity
  last_heartbeat     (string)    ISO 8601 timestamp
  status             (string)    "active", "draining", "offline"

Example:
  HSET ade:lock:agent:agent-123:capacity \
    max_tasks 10 \
    active_tasks 3 \
    queued_tasks 1 \
    last_heartbeat "2026-03-18T15:30:00Z" \
    status "active"

Lock Queue Keys

Lock Wait Queue (Ordered list of waiting agents)

Key: ade:lock:task:{task_id}:queue
Type: Sorted Set (ZSET)
TTL: 5 minutes (cleaned up when lock released)

Score: Unix timestamp (millisecond precision for FIFO ordering)
Value: JSON object

Value Format:
  {
    "agent_id": "agent-uuid",
    "mode": "exclusive",
    "priority": 100,
    "requested_at": "2026-03-18T15:30:00Z",
    "max_wait_seconds": 60
  }

Example:
  ZADD ade:lock:task:550e8400-e29b-41d4-a716-446655440000:queue \
    1710775800000 '{"agent_id":"agent-789","mode":"exclusive","priority":100,...}'

Lock Notification Channel

Key: ade:lock:task:{task_id}:channel
Type: Pub/Sub Channel

Events:
  "acquired:{agent_id}"      - Lock acquired
  "released:{agent_id}"      - Lock released
  "expired"                  - Lock expired
  "queued:{agent_id}"        - Agent added to queue
  "promoted:{agent_id}"      - Agent promoted from queue

Lock Index Keys

Active Locks by Agent (Reverse index)

Key: ade:lock:index:agent:{agent_id}
Type: Set
TTL: Matches individual lock TTLs

Members: Lock key references
  ade:lock:task:{task_id}
  ade:lock:resource:{type}:{id}

Purpose: Quick lookup of all locks held by an agent

Active Locks by Resource Type

Key: ade:lock:index:resource:{resource_type}
Type: Set
TTL: Matches individual lock TTLs

Members: Resource lock keys
  ade:lock:resource:database:prod-db-01
  ade:lock:resource:service:api-gateway

Global Lock Registry

Key: ade:lock:registry
Type: Sorted Set
TTL: None

Score: Expiration timestamp
Value: Lock key

Purpose: Background cleanup of expired locks
Example:
  ZADD ade:lock:registry 1710775830 "ade:lock:task:550e8400-..."

Deadlock Detection Keys

Wait-For Graph Edge

Key: ade:lock:waitfor:{agent_id}
Type: Set
TTL: 5 minutes

Members: Lock keys the agent is waiting for
  ade:lock:task:{task_id}
  ade:lock:resource:{type}:{id}

Purpose: Build wait-for graph for deadlock detection

Deadlock Detection Timestamp

Key: ade:lock:deadlock:check:{agent_id}
Type: String
TTL: 30 seconds

Value: ISO 8601 timestamp of last deadlock check

Purpose: Rate limit deadlock detection attempts

Approval Keys

Approval Request Keys

Approval Request Data

Key: ade:approval:request:{approval_id}
Type: Hash
TTL: 30 days (archived after completion)

Fields:
  task_id            (string)    UUID of associated task
  reviewer_id        (string)    User ID of assigned reviewer
  reviewer_name      (string)    Display name
  status             (string)    "PENDING", "APPROVED", "REJECTED", "DELEGATED"
  priority           (string)    "LOW", "NORMAL", "HIGH", "URGENT"
  delegated_to       (string)    User ID (if delegated)
  delegation_chain   (JSON)      Array of user IDs in delegation chain
  created_at         (string)    ISO 8601 timestamp
  due_at             (string)    ISO 8601 timestamp
  responded_at       (string)    ISO 8601 timestamp
  response_action    (string)    "approve", "reject", "request_changes"
  response_reason    (string)    Free text explanation
  reviewed_by        (string)    Final responding user ID

Example:
  HSET ade:approval:request:app-123 \
    task_id "task-456" \
    reviewer_id "user-789" \
    status "PENDING" \
    priority "HIGH" \
    created_at "2026-03-18T15:30:00Z" \
    due_at "2026-03-20T15:30:00Z"

Approval Queue Keys

User Approval Queue (Pending approvals for a user)

Key: ade:approval:queue:user:{user_id}
Type: Sorted Set
TTL: None (entries expire based on approval TTL)

Score: Priority score (higher = more urgent)
  Calculated as: (risk_score * 10) + priority_bonus
  priority_bonus: URGENT=1000, HIGH=500, NORMAL=100, LOW=0

Value: approval_id

Example:
  ZADD ade:approval:queue:user:user-789 850 "app-123"
  ZADD ade:approval:queue:user:user-789 450 "app-124"

Task Approval Index (All approvals for a task)

Key: ade:approval:index:task:{task_id}
Type: Set
TTL: Matches approval data TTL

Members: approval_ids
  app-123
  app-124
  app-125

Global Approval Queue (All pending approvals)

Key: ade:approval:queue:global
Type: Sorted Set
TTL: None

Score: Due timestamp (Unix seconds)
Value: approval_id

Purpose: Background worker for escalation/timeout handling

Approval Statistics Keys

User Approval Stats

Key: ade:approval:stats:user:{user_id}
Type: Hash
TTL: None (rolling window)

Fields:
  pending_count      (integer)   Current pending approvals
  approved_today     (integer)   Approvals given today
  rejected_today     (integer)   Rejections given today
  avg_response_time  (float)     Average response time in seconds
  last_action_at     (string)    ISO 8601 timestamp

Note: Daily counters reset at midnight UTC via background job

Task Approval Stats

Key: ade:approval:stats:task:{task_id}
Type: Hash
TTL: 30 days

Fields:
  required_count     (integer)   Required approvals
  approved_count     (integer)   Current approvals
  rejected_count     (integer)   Current rejections
  pending_count      (integer)   Awaiting response
  quorum_reached     (boolean)   Whether minimum approvals met

Delegation Keys

User Delegation Policy

Key: ade:approval:delegation:{user_id}:{policy_id}
Type: Hash
TTL: Based on policy expiration

Fields:
  owner_id           (string)    Policy owner
  delegate_to        (string)    Delegated reviewer
  conditions         (JSON)      Matching conditions
  cascade            (boolean)   Allow further delegation
  active             (boolean)   Policy enabled/disabled
  created_at         (string)    ISO 8601 timestamp
  expires_at         (string)    ISO 8601 timestamp

Example:
  HSET ade:approval:delegation:user-123:policy-456 \
    owner_id "user-123" \
    delegate_to "user-789" \
    conditions '{"task_types":["infrastructure"],"risk_above":50}' \
    cascade "true" \
    active "true"

Delegation Policy Index

Key: ade:approval:delegation:index:{user_id}
Type: Set
TTL: None

Members: policy_ids for the user
  policy-456
  policy-789

Task Keys

Task Data Keys

Task State

Key: ade:task:{task_id}:state
Type: String
TTL: 90 days

Value: Current state
  DRAFT, SUBMITTED, REVIEWING, APPROVED, APPLYING, COMPLETED, REJECTED, CANCELLED

Example:
  SET ade:task:task-123:state "REVIEWING"

Task Data (Full object)

Key: ade:task:{task_id}:data
Type: JSON (RedisJSON module) or String (serialized JSON)
TTL: 90 days

Value: Complete task object including config, metadata, execution results

Note: For Redis versions without JSON module, store as serialized string

Task Configuration (Immutable)

Key: ade:task:{task_id}:config
Type: Hash
TTL: 90 days

Fields:
  type               (string)    Task type
  version            (string)    Config version
  description        (string)    Human-readable description
  parameters         (JSON)      Task parameters
  resources          (JSON)      Array of resource references
  rollback_strategy  (string)    "automatic", "manual", "none"
  timeout_seconds    (integer)   Execution timeout
  priority           (integer)   0-100 priority score

Task Metadata

Key: ade:task:{task_id}:metadata
Type: Hash
TTL: 90 days

Fields:
  author_id          (string)    Creating user
  author_name        (string)    Display name
  team               (string)    Team/organization
  ticket_ref         (string)    External ticket reference
  tags               (JSON)      Array of string tags
  created_at         (string)    ISO 8601 timestamp
  updated_at         (string)    ISO 8601 timestamp
  submitted_at       (string)    ISO 8601 timestamp
  approved_at        (string)    ISO 8601 timestamp
  applying_at        (string)    ISO 8601 timestamp
  completed_at       (string)    ISO 8601 timestamp

Task State Index Keys

Tasks by State

Key: ade:task:index:state:{state}
Type: Sorted Set
TTL: None (members removed on state change)

Score: created_at timestamp (Unix seconds)
Value: task_id

Example Keys:
  ade:task:index:state:DRAFT
  ade:task:index:state:REVIEWING
  ade:task:index:state:APPROVED

Tasks by Author

Key: ade:task:index:author:{user_id}
Type: Sorted Set
TTL: 90 days

Score: created_at timestamp
Value: task_id

Tasks by Resource

Key: ade:task:index:resource:{resource_type}:{resource_id}
Type: Sorted Set
TTL: 90 days

Score: created_at timestamp
Value: task_id

Example:
  ade:task:index:resource:database:prod-db-01

Tasks by Tag

Key: ade:task:index:tag:{tag_name}
Type: Sorted Set
TTL: 90 days

Score: created_at timestamp
Value: task_id

Task Execution Keys

Task Execution Status

Key: ade:task:{task_id}:execution
Type: Hash
TTL: 90 days

Fields:
  started_at         (string)    ISO 8601 timestamp
  completed_at       (string)    ISO 8601 timestamp
  agent_id           (string)    Executing agent
  result             (string)    "success", "failure", "timeout", "cancelled"
  output             (string)    Execution output (truncated)
  output_key         (string)    Key to full output in S3/blob storage
  error              (string)    Error message (if failed)
  error_details      (JSON)      Structured error information
  retry_count        (integer)   Number of retry attempts

Task Preview Results

Key: ade:task:{task_id}:preview
Type: JSON/String
TTL: 7 days

Value: Preview result object with changes, warnings, errors

Task Risk Assessment

Key: ade:task:{task_id}:risk
Type: Hash
TTL: 90 days

Fields:
  score              (integer)   0-100 risk score
  level              (string)    "LOW", "MEDIUM", "HIGH", "CRITICAL"
  factors            (JSON)      Array of risk factors
  auto_approvable    (boolean)   Can skip human review
  assessed_at        (string)    ISO 8601 timestamp
  assessed_by        (string)    Algorithm version

Session Keys

User Session

Key: ade:session:{session_id}
Type: Hash
TTL: 24 hours

Fields:
  user_id            (string)    Authenticated user
  user_name          (string)    Display name
  roles              (JSON)      Array of role strings
  permissions        (JSON)      Array of permission strings
  created_at         (string)    ISO 8601 timestamp
  last_active        (string)    ISO 8601 timestamp
  ip_address         (string)    Client IP
  user_agent         (string)    Client user agent

User Active Sessions

Key: ade:session:index:user:{user_id}
Type: Set
TTL: 24 hours

Members: session_ids

Rate Limiting Keys

API Rate Limit

Key: ade:ratelimit:{endpoint}:{user_id}
Type: String (counter) or Redis Cell (if available)
TTL: 1 minute (sliding window)

Value: Request count

Example:
  ade:ratelimit:tasks:create:user-123
  ade:ratelimit:approvals:respond:user-456

Lock Acquisition Rate Limit (per agent)

Key: ade:ratelimit:lock:acquire:{agent_id}
Type: String (counter)
TTL: 1 minute

Value: Lock acquisition attempts

Purpose: Prevent lock starvation attacks

Event Keys

Event Stream (Redis Streams)

Key: ade:events:{event_type}
Type: Stream
TTL: 7 days (MAXLEN ~10000)

Event Types:
  ade:events:task
  ade:events:approval
  ade:events:lock

Entry Fields:
  event              (string)    Event name
  timestamp          (string)    ISO 8601 timestamp
  payload            (JSON)      Event data
  source             (string)    Service/agent that generated event

Example:
  XADD ade:events:task * \
    event "task:state_changed" \
    timestamp "2026-03-18T15:30:00Z" \
    payload '{"task_id":"...","from":"DRAFT","to":"SUBMITTED"}' \
    source "api-server-01"

Event Consumer Groups

Key: ade:events:{event_type}:consumers
Type: Stream Consumer Group

Groups:
  notification-service
  audit-logger
  webhook-dispatcher
  analytics-pipeline

Background Job Keys

Job Queue

Key: ade:job:queue:{queue_name}
Type: List or Sorted Set
TTL: None

Queues:
  ade:job:queue:lock_cleanup      - Expired lock cleanup
  ade:job:queue:approval_timeout  - Approval escalation
  ade:job:queue:task_timeout      - Task execution timeout
  ade:job:queue:deadlock_detect   - Deadlock detection
  ade:job:queue:archive           - Old data archival

Scheduled Jobs

Key: ade:job:scheduled
Type: Sorted Set
TTL: None

Score: Execution timestamp (Unix seconds)
Value: JSON job description

Example:
  ZADD ade:job:scheduled 1710776400 \
    '{"type":"lock_cleanup","target":"ade:lock:task:123"}'

Job Locks (prevent duplicate job execution)

Key: ade:job:lock:{job_id}
Type: String
TTL: Job execution timeout

Value: Worker instance ID

Configuration Keys

System Configuration

Key: ade:config:{config_name}
Type: String or Hash
TTL: None

Configs:
  ade:config:lock:default_ttl          (integer, seconds)
  ade:config:lock:max_ttl              (integer, seconds)
  ade:config:lock:heartbeat_interval   (integer, seconds)
  ade:config:approval:default_timeout  (integer, seconds)
  ade:config:approval:max_timeout      (integer, seconds)
  ade:config:task:default_timeout      (integer, seconds)
  ade:config:risk:thresholds           (JSON)

Feature Flags

Key: ade:feature:{flag_name}
Type: String
TTL: None

Value: "enabled" or "disabled"

Examples:
  ade:feature:auto_approve_low_risk
  ade:feature:deadlock_detection
  ade:feature:batch_approvals

Key Lifecycle Summary

Key Pattern Type Default TTL Cleanup Strategy
ade:lock:* (active) Hash 30s Heartbeat extends, expires auto-release
ade:lock:*:queue ZSET 5m Cleared on lock release
ade:lock:registry ZSET None Background job cleans expired
ade:approval:request:* Hash 30d Archived, then deleted
ade:approval:queue:* ZSET None Entries removed on status change
ade:task:*:state String 90d Archived to cold storage
ade:task:*:data JSON 90d Archived to cold storage
ade:task:index:* ZSET 90d Cleared on task deletion
ade:session:* Hash 24h Auto-expire
ade:events:* Stream 7d MAXLEN eviction
ade:ratelimit:* String 1m Auto-expire

Redis Commands Reference

Lock Operations

# Acquire lock (with NX - only if not exists)
HSET ade:lock:task:123 \
  holder_agent_id agent-001 \
  acquired_at "2026-03-18T15:30:00Z" \
  expires_at "2026-03-18T15:30:30Z" \
  NX

# Extend lock TTL
HEXPIRE ade:lock:task:123 30

# Check lock
HGETALL ade:lock:task:123

# Release lock (use Lua for atomic check-and-delete)
# Lua script:
# if redis.call('hget', KEYS[1], 'holder_agent_id') == ARGV[1] then
#   return redis.call('del', KEYS[1])
# end
# return 0

# Add to queue
ZADD ade:lock:task:123:queue 1710775800000 '{"agent_id":"agent-002",...}'

# Get next waiter
ZPOPMIN ade:lock:task:123:queue 1

Approval Operations

# Create approval request
HSET ade:approval:request:app-123 \
  task_id task-456 \
  reviewer_id user-789 \
  status PENDING

# Add to user queue
ZADD ade:approval:queue:user:user-789 850 app-123

# Record response
HSET ade:approval:request:app-123 \
  status APPROVED \
  responded_at "2026-03-18T16:00:00Z" \
  response_action approve

# Remove from queue
ZREM ade:approval:queue:user:user-789 app-123

Task Operations

# Create task
SET ade:task:task-123:state DRAFT
HSET ade:task:task-123:metadata \
  author_id user-001 \
  created_at "2026-03-18T15:00:00Z"

# Update state (atomic)
SET ade:task:task-123:state REVIEWING
ZREM ade:task:index:state:DRAFT task-123
ZADD ade:task:index:state:REVIEWING 1710774000 task-123

# Get task with all data
HMGET ade:task:task-123:metadata author_id created_at
GET ade:task:task-123:state

Cluster Mode Considerations

When using Redis Cluster, ensure related keys are on the same hash slot using hash tags:

ade:{task:123}:state        → hash slot for "task:123"
ade:{task:123}:data         → same slot
ade:{task:123}:execution    → same slot
ade:lock:task:{task:123}    → hash slot for "task:123"
ade:approval:index:task:{task:123} → hash slot for "task:123"

This enables multi-key operations (transactions, Lua scripts) on related data.


Migration Notes

From v1 to v2

  • Renamed lock:* to ade:lock:* for namespacing
  • Changed approval status from integers to strings
  • Added JSON support for complex fields (requires RedisJSON or serialization)

Backup Strategy

# Daily RDB snapshot
# Real-time AOF for point-in-time recovery
# Cross-region replication for disaster recovery