feat(matrix-bridge-dagi): harden mixed rooms with safe defaults and ops visibility (M2.2)
Guard rails (mixed_routing.py):
- MAX_AGENTS_PER_MIXED_ROOM (default 5): fail-fast at parse time
- MAX_SLASH_LEN (default 32): reject garbage/injection slash tokens
- Unified rejection reasons: unknown_agent, slash_too_long, no_mapping
- REASON_REJECTED_* constants (separate from success REASON_*)
Ingress (ingress.py):
- per-room-agent concurrency semaphore (MIXED_CONCURRENCY_CAP, default 1)
- active_lock_count property for /health + prometheus
- UNKNOWN_AGENT_BEHAVIOR: "ignore" (silent) | "reply_error" (inform user)
- on_routed(agent_id, reason) callback for routing metrics
- on_route_rejected(room_id, reason) callback for rejection metrics
- matrix.route.rejected audit event on every rejection
Config + main:
- max_agents_per_mixed_room, max_slash_len, unknown_agent_behavior, mixed_concurrency_cap
- matrix_bridge_routed_total{agent_id, reason} counter
- matrix_bridge_route_rejected_total{room_id, reason} counter
- matrix_bridge_active_room_agent_locks gauge
- /health: mixed_guard_rails section + total_agents_in_mixed_rooms
- docker-compose: all 4 new guard rail env vars
Runbook: section 9 — mixed room debug guide (6 acceptance tests, routing metrics, session isolation, lock hang, config guard)
Tests: 108 pass (94 → 108, +14 new tests for guard rails + callbacks + concurrency)
Made-with: Cursor
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
|
||||
**Сервіс:** `matrix-bridge-dagi` | **Нода:** NODA1 | **Порт:** 7030 (localhost)
|
||||
**Stack:** Matrix (Synapse) → bridge → Router `/v1/agents/{id}/infer` → Matrix reply
|
||||
**Фаза:** M1 (1 room = Sofiia), H1/H2/H3 hardening активний
|
||||
**Фаза:** M2.2 (N rooms + mixed room routing), H1/H2/H3 hardening активний
|
||||
|
||||
---
|
||||
|
||||
@@ -364,11 +364,167 @@ echo | openssl s_client -connect matrix.daarion.space:443 -servername matrix.daa
|
||||
|
||||
---
|
||||
|
||||
---
|
||||
|
||||
## 9. Mixed Room Debug Guide (M2.2)
|
||||
|
||||
### 9.1 Перевірка конфігурації mixed rooms
|
||||
|
||||
```bash
|
||||
# Поточний mapping (regular + mixed)
|
||||
curl -sS http://127.0.0.1:7030/bridge/mappings | python3 -m json.tool
|
||||
|
||||
# Guard rail параметри (з /health)
|
||||
curl -sS http://127.0.0.1:7030/health | python3 -m json.tool | python3 -c "
|
||||
import sys, json; h=json.load(sys.stdin)
|
||||
print('mixed_rooms:', h.get('mixed_rooms_count',0))
|
||||
print('total_agents:', h.get('total_agents_in_mixed_rooms',0))
|
||||
print('guard_rails:', json.dumps(h.get('mixed_guard_rails',{}), indent=2))
|
||||
"
|
||||
```
|
||||
|
||||
### 9.2 Smoke test для mixed room (6 acceptance test cases)
|
||||
|
||||
Відправляємо з `test_user` у mixed room `!roomX:daarion.space`:
|
||||
|
||||
```bash
|
||||
# Змінна для зручності
|
||||
ROOM_ID="!roomX:daarion.space"
|
||||
TOKEN="@test_user_token" # або через Element UI
|
||||
|
||||
# 1. Slash → Sofiia
|
||||
curl -sX POST "https://matrix.daarion.space/_matrix/client/v3/rooms/${ROOM_ID}/send/m.room.message/txn1" \
|
||||
-H "Authorization: Bearer $TOKEN" -H "Content-Type: application/json" \
|
||||
-d '{"msgtype":"m.text","body":"/sofiia ping"}' | jq '.event_id'
|
||||
|
||||
# 2. Slash → Helion
|
||||
curl -sX POST "...txn2" -d '{"msgtype":"m.text","body":"/helion ping"}' | jq '.event_id'
|
||||
|
||||
# 3. @mention → Sofiia
|
||||
curl -sX POST "...txn3" -d '{"msgtype":"m.text","body":"@sofiia status"}' | jq '.event_id'
|
||||
|
||||
# 4. colon-mention → Sofiia
|
||||
curl -sX POST "...txn4" -d '{"msgtype":"m.text","body":"sofiia: status"}' | jq '.event_id'
|
||||
|
||||
# 5. Plain text → default agent
|
||||
curl -sX POST "...txn5" -d '{"msgtype":"m.text","body":"ping"}' | jq '.event_id'
|
||||
|
||||
# 6. Unknown slash → audit matrix.route.rejected
|
||||
curl -sX POST "...txn6" -d '{"msgtype":"m.text","body":"/unknown test"}' | jq '.event_id'
|
||||
```
|
||||
|
||||
**Очікувана поведінка у кімнаті:**
|
||||
| Команда | Reply prefix | Router agent |
|
||||
|---------|-------------|--------------|
|
||||
| `/sofiia ping` | `Sofiia: ...` | sofiia |
|
||||
| `/helion ping` | `Helion: ...` | helion |
|
||||
| `@sofiia status` | `Sofiia: ...` | sofiia |
|
||||
| `sofiia: status` | `Sofiia: ...` | sofiia |
|
||||
| `ping` (plain) | `<DefaultAgent>: ...` | перший у списку |
|
||||
| `/unknown test` | `⚠️ Unknown agent...` або тиша (залежно від `UNKNOWN_AGENT_BEHAVIOR`) | — |
|
||||
|
||||
### 9.3 Перевірка routing метрик
|
||||
|
||||
```bash
|
||||
# Successful routing breakdown by reason
|
||||
curl -sS http://127.0.0.1:7030/metrics | grep 'matrix_bridge_routed_total'
|
||||
# Очікування:
|
||||
# matrix_bridge_routed_total{agent_id="sofiia",reason="slash_command"} N
|
||||
# matrix_bridge_routed_total{agent_id="helion",reason="slash_command"} N
|
||||
# matrix_bridge_routed_total{agent_id="sofiia",reason="at_mention"} N
|
||||
# matrix_bridge_routed_total{agent_id="sofiia",reason="default"} N
|
||||
|
||||
# Rejections
|
||||
curl -sS http://127.0.0.1:7030/metrics | grep 'matrix_bridge_route_rejected_total'
|
||||
# Очікується > 0 лише якщо були /unknown або занадто довгі токени
|
||||
|
||||
# Active concurrency locks
|
||||
curl -sS http://127.0.0.1:7030/metrics | grep 'active_room_agent_locks'
|
||||
# Зазвичай 0 (між повідомленнями)
|
||||
```
|
||||
|
||||
### 9.4 Debug: "Wrong agent responds"
|
||||
|
||||
**Симптом:** У mixed room `/helion ...` → відповідає sofiia, або відповідає не той агент.
|
||||
|
||||
**Діагностика:**
|
||||
|
||||
```bash
|
||||
# 1. Перевірити audit events в sofiia-console
|
||||
# (через psql або API)
|
||||
curl -sS http://127.0.0.1:8002/api/audit \
|
||||
| python3 -m json.tool | grep -A5 '"event":"matrix.message.received"' \
|
||||
| grep '"routing_reason"'
|
||||
# routing_reason має бути "slash_command", "at_mention", "colon_mention" або "default"
|
||||
|
||||
# 2. Перевірити логи bridge
|
||||
docker logs matrix-bridge-dagi-node1 --tail 100 2>&1 | grep -E 'route|Route|routing'
|
||||
# Очікування: "Slash route: /helion → helion" або "Default route: → sofiia"
|
||||
|
||||
# 3. Перевірити BRIDGE_MIXED_ROOM_MAP в .env
|
||||
grep BRIDGE_MIXED_ROOM_MAP /opt/microdao-daarion/.env
|
||||
# Формат: "!roomX:server=sofiia,helion"
|
||||
# Перший у списку = default agent
|
||||
```
|
||||
|
||||
**Виправлення:**
|
||||
- Якщо порядок агентів неправильний — змінити `BRIDGE_MIXED_ROOM_MAP` або встановити `BRIDGE_MIXED_DEFAULTS`
|
||||
- Перезапустити bridge: `docker restart matrix-bridge-dagi-node1`
|
||||
|
||||
### 9.5 Debug: "Session context змішується між агентами"
|
||||
|
||||
**Симптом:** Helion "пам'ятає" контекст розмови sofiia.
|
||||
|
||||
**Перевірка:** Session key у логах (`session_id` у invoke payload)
|
||||
|
||||
```bash
|
||||
docker logs matrix-bridge-dagi-node1 --tail 50 2>&1 | grep session_id
|
||||
# Очікування для mixed room:
|
||||
# session_id = "matrix:roomX_daarion_space:sofiia" ← ізольований per-agent
|
||||
# session_id = "matrix:roomX_daarion_space:helion" ← окремий контекст
|
||||
```
|
||||
|
||||
Якщо обидва агенти мають однаковий `session_id` — це баг рефакторингу, відкати на M2.0.
|
||||
|
||||
### 9.6 Debug: Concurrency lock "застрявання"
|
||||
|
||||
**Симптом:** Запит зависає, не відповідає, active_lock_count > 0 протягом >60s.
|
||||
|
||||
```bash
|
||||
# Перевірити active locks
|
||||
curl -sS http://127.0.0.1:7030/health | python3 -m json.tool | python3 -c \
|
||||
"import sys,json; h=json.load(sys.stdin); print(h.get('mixed_guard_rails',{}).get('active_room_agent_locks',0))"
|
||||
|
||||
# Якщо > 0 протягом довгого часу — Router застряг
|
||||
curl -sS http://127.0.0.1:9102/health | jq '.status'
|
||||
|
||||
# Якщо Router недоступний — перезапуск bridge звільнить locks (graceful shutdown + cancel)
|
||||
docker restart matrix-bridge-dagi-node1
|
||||
```
|
||||
|
||||
### 9.7 Guard rail: перевірка MAX_AGENTS_PER_MIXED_ROOM
|
||||
|
||||
Якщо у `.env` є рядок з 6+ агентами і `MAX_AGENTS_PER_MIXED_ROOM=5`:
|
||||
|
||||
```bash
|
||||
docker logs matrix-bridge-dagi-node1 --tail 20 2>&1 | grep 'Config error\|MAX_AGENTS'
|
||||
# Очікується: "❌ Config error: BRIDGE_MIXED_ROOM_MAP parse errors: Room ... has 6 agents > MAX..."
|
||||
# Bridge не стартує → /health поверне {"ok":false,"error":"..."}
|
||||
```
|
||||
|
||||
**Виправлення:** Зменшити кількість агентів або збільшити `MAX_AGENTS_PER_MIXED_ROOM`.
|
||||
|
||||
---
|
||||
|
||||
## 8. Що прикріпити до інциденту
|
||||
|
||||
```bash
|
||||
echo "=== /health ===" && curl -sS http://127.0.0.1:7030/health | python3 -m json.tool
|
||||
echo "=== /metrics traffic ===" && curl -sS http://127.0.0.1:7030/metrics | grep -E 'matrix_bridge_(messages|rate_limited|queue_dropped|gateway_errors)'
|
||||
echo "=== /metrics latency ===" && curl -sS http://127.0.0.1:7030/metrics | grep -E '(invoke|send|queue_wait)_duration_seconds_(count|sum)'
|
||||
echo "=== logs ===" && docker logs matrix-bridge-dagi-node1 --tail 50 2>&1 | grep -E 'ERROR|WARN|rate_limited|queue_full|Reply sent|invoke ok'
|
||||
echo "=== /bridge/mappings ===" && curl -sS http://127.0.0.1:7030/bridge/mappings | python3 -m json.tool
|
||||
echo "=== /metrics traffic ===" && curl -sS http://127.0.0.1:7030/metrics \
|
||||
| grep -E 'matrix_bridge_(messages|rate_limited|queue_dropped|gateway_errors|routed|route_rejected)'
|
||||
echo "=== /metrics latency ===" && curl -sS http://127.0.0.1:7030/metrics \
|
||||
| grep -E '(invoke|send|queue_wait)_duration_seconds_(count|sum)'
|
||||
echo "=== logs ===" && docker logs matrix-bridge-dagi-node1 --tail 50 2>&1 \
|
||||
| grep -E 'ERROR|WARN|rate_limited|queue_full|Reply sent|invoke ok|route|rejected'
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user