# RabbitMQ Queue Bindings - Quick Reference

**Last Updated:** 2026-02-26  
**Related Fix:** Executor events queue separation (event.created only)

## Overview

This document provides a quick reference for understanding RabbitMQ queue bindings in Attune after the inquiry queue separation fix.

## Exchange Topology

Attune uses three main exchanges:

1. **`attune.events`** (Topic) - Event messages from sensors
2. **`attune.executions`** (Topic) - Execution and enforcement messages
3. **`attune.notifications`** (Fanout) - System notifications

## Queue → Routing Key Bindings

### Events Exchange (`attune.events`)

| Queue | Routing Key | Message Type | Consumer |
|-------|-------------|--------------|----------|
| `attune.events.queue` | `#` (all) | All event types | Sensor service (rule lifecycle) |
| `attune.executor.events.queue` | `event.created` | `EventCreatedPayload` | EventProcessor (executor) |
| `attune.rules.lifecycle.queue` | `rule.created`, `rule.enabled`, `rule.disabled` | `RuleCreated/Enabled/DisabledPayload` | RuleLifecycleListener (sensor) |
| `worker.{id}.packs` | `pack.registered` | `PackRegisteredPayload` | Worker (per-instance) |

> **Note:** The sensor's `attune.events.queue` is bound with `#` (all routing keys) for catch-all
> event monitoring. The executor uses a dedicated `attune.executor.events.queue` bound only to
> `event.created` to avoid deserializing unrelated message types (rule lifecycle, pack registration).

### Executions Exchange (`attune.executions`)

| Queue | Routing Key | Message Type | Consumer |
|-------|-------------|--------------|----------|
| `attune.enforcements.queue` | `enforcement.#` | `EnforcementCreatedPayload` | EnforcementProcessor (executor) |
| `attune.execution.requests.queue` | `execution.requested` | `ExecutionRequestedPayload` | ExecutionScheduler (executor) |
| `attune.execution.status.queue` | `execution.status.changed` | `ExecutionStatusChangedPayload` | ExecutionManager (executor) |
| `attune.execution.completed.queue` | `execution.completed` | `ExecutionCompletedPayload` | CompletionListener (executor) |
| `attune.inquiry.responses.queue` | `inquiry.responded` | `InquiryRespondedPayload` | InquiryHandler (executor) |

### Notifications Exchange (`attune.notifications`)

| Queue | Routing Key | Message Type | Consumer |
|-------|-------------|--------------|----------|
| `attune.notifications.queue` | (none - fanout) | `NotificationCreatedPayload` | Various |

## Message Publishers

### Worker Service
- **`execution.status.changed`** → Published during execution with status updates
- **`execution.completed`** → Published when execution finishes (success/failure)

### API Service
- **`inquiry.responded`** → Published when user responds to an inquiry
- **`inquiry.created`** → Published when inquiry is created (executor publishes this)

### Executor Service
- **`enforcement.created`** → Published when rule triggers
- **`execution.requested`** → Published to schedule execution
- **`inquiry.created`** → Published when action requests human input

### Sensor Service
- **`event.created`** → Published when trigger condition is met

## Fixed Issues

### ✅ Fixed: inquiry_id Deserialization Error

**Problem:** InquiryHandler was consuming from `execution.status.queue`, receiving wrong message types (ExecutionCompletedPayload instead of InquiryRespondedPayload).

**Solution:** Created dedicated `attune.inquiry.responses.queue` bound to `inquiry.responded`.

**Status:** Fixed in 2026-02-03.

### ✅ Fixed: action_id Deserialization Error

**Problem:** ExecutionManager was consuming from `execution.status.queue` along with CompletionListener, receiving wrong message types (ExecutionCompletedPayload with action_id instead of ExecutionStatusChangedPayload).

**Solution:** Created dedicated `attune.execution.completed.queue` bound to `execution.completed` for CompletionListener. ExecutionManager now exclusively consumes from `attune.execution.status.queue`.

**Status:** Fixed in 2026-02-03.

## Message Flow Examples

### Execution Completion Flow
```
Worker → ExecutionCompletedPayload
       → routing key: "execution.completed"
       → exchange: "attune.executions"
       → queue: "attune.execution.completed.queue"
       → consumer: CompletionListener (executor)
       → action: Release queue slot, notify waiting executions
```

### Inquiry Response Flow
```
API → InquiryRespondedPayload
    → routing key: "inquiry.responded"
    → exchange: "attune.executions"
    → queue: "attune.inquiry.responses.queue"
    → consumer: InquiryHandler (executor)
    → action: Resume paused execution with inquiry response
```

### Execution Status Update Flow
```
Worker → ExecutionStatusChangedPayload
       → routing key: "execution.status.changed"
       → exchange: "attune.executions"
       → queue: "attune.execution.status.queue"
       → consumer: ExecutionManager (executor)
       → action: Update execution record, trigger child executions
```

## Consumer Tags

Each consumer has a unique tag for identification:

- `executor.event` - EventProcessor
- `executor.enforcement` - EnforcementProcessor
- `executor.scheduler` - ExecutionScheduler
- `executor.completion` - CompletionListener
- `executor.manager` - ExecutionManager
- `executor.inquiry` - InquiryHandler

## Debugging Tips

### Check Queue Bindings
```bash
# Via RabbitMQ Management CLI
rabbitmqadmin list bindings

# Via Management UI
http://localhost:15672 → Exchanges → attune.executions → Bindings

# Check specific queue
rabbitmqadmin list bindings source=attune.executions
```

### Monitor Message Routing
```bash
# View messages in queue
rabbitmqadmin get queue=attune.inquiry.responses.queue count=10

# Check consumer connections
rabbitmqadmin list consumers
```

### Check for Deserialization Errors
```bash
# Grep executor logs
grep "Failed to deserialize message" logs/executor.log

# Look for specific field errors
grep "missing field.*inquiry_id" logs/executor.log
grep "missing field.*action_id" logs/executor.log
```

## Configuration

Queue configuration is defined in:
- **File:** `attune/crates/common/src/mq/config.rs`
- **Struct:** `QueuesConfig`

Bindings are established in:
- **File:** `attune/crates/common/src/mq/connection.rs`
- **Function:** `Connection::setup_infrastructure()`

## Best Practices

1. **One queue, one message type** - Avoid multiple message schemas per queue
2. **One queue, one consumer** - Avoid competing consumers on the same queue
3. **Use specific routing keys** - Prefer `execution.completed` over `execution.#`
4. **Canonical message types** - Use structs from `attune_common::mq::messages`
5. **Monitor dead letter queues** - Check DLQ for routing/deserialization failures

## Summary of Queue Architecture

**Before Fix:** Multiple consumers competing on same queue
- `attune.execution.status.queue` had 3 consumers with 3 different message types ❌

**After Fix:** One queue, one consumer, one message type
- `attune.execution.status.queue` → ExecutionManager only ✅
- `attune.execution.completed.queue` → CompletionListener only ✅
- `attune.inquiry.responses.queue` → InquiryHandler only ✅

## Related Documentation

- `attune/work-summary/2026-02-03-inquiry-queue-separation.md` - Complete fix details
- `attune/docs/architecture/queue-architecture.md` - Overall architecture
- `attune/crates/common/src/mq/messages.rs` - Message type definitions