feat: Node Self-Healing, DAGI Audit, Agent Prompts, Infra Invariants

### Backend (city-service)
- Node Registry + Self-Healing API (migration 039)
- Improved get_all_nodes() with robust fallback for node_registry/node_cache
- Agent Prompts Runtime API for DAGI Router integration
- DAGI Router Audit endpoints (phantom/stale detection)
- Node Agents API (Guardian/Steward)
- Node metrics extended (CPU/GPU/RAM/Disk)

### Frontend (apps/web)
- Node Directory with improved error handling
- Node Cabinet with metrics cards
- DAGI Router Card component
- Node Metrics Card component
- useDAGIAudit hook

### Scripts
- check-invariants.py - deploy verification
- node-bootstrap.sh - node self-registration
- node-guardian-loop.py - continuous self-healing
- dagi_agent_audit.py - DAGI audit utility

### Migrations
- 034: Agent prompts seed
- 035: Agent DAGI audit
- 036: Node metrics extended
- 037: Node agents complete
- 038: Agent prompts full coverage
- 039: Node registry self-healing

### Tests
- test_infra_smoke.py
- test_agent_prompts_runtime.py
- test_dagi_router_api.py

### Documentation
- DEPLOY_CHECKLIST_2024_11_30.md
- Multiple TASK_PHASE docs
This commit is contained in:
Apple
2025-11-30 13:52:01 -08:00
parent 0c7836af5a
commit bca81dc719
36 changed files with 10630 additions and 55 deletions

View File

@@ -0,0 +1,244 @@
# 🚀 DEPLOY CHECKLIST — daarion.space
**Дата:** 2024-11-30
**Версія:** MVP Node Self-Healing + DAGI Audit + Agent Prompts
---
## 📋 Що деплоїмо
### Backend (city-service)
- ✅ Node Registry + Self-Healing API
- ✅ Improved `get_all_nodes()` з fallback
- ✅ Agent Prompts Runtime API
- ✅ DAGI Router Audit API
- ✅ Node Agents API (Guardian/Steward)
### Frontend (apps/web)
- ✅ Node Directory з покращеним error handling
- ✅ Node Cabinet з метриками
- ✅ DAGI Router Card
- ✅ Node Metrics Card
### Scripts
-`check-invariants.py` — перевірка інваріантів
-`node-bootstrap.sh` — самореєстрація ноди
-`node-guardian-loop.py` — self-healing loop
### Міграції (НОВІ)
- `034_agent_prompts_seed.sql`
- `035_agent_dagi_audit.sql`
- `036_node_metrics_extended.sql`
- `037_node_agents_complete.sql`
- `038_agent_prompts_full_coverage.sql`
- `039_node_registry_self_healing.sql`
---
## 🔧 КРОК 1: Локально — Закомітити та запушити
```bash
cd /Users/apple/github-projects/microdao-daarion
# Додати всі зміни
git add .
# Закомітити
git commit -m "feat: Node Self-Healing, DAGI Audit, Agent Prompts, Infra Invariants
- Node Registry for self-healing (migration 039)
- Improved get_all_nodes() with robust fallback
- Agent Prompts Runtime API for DAGI Router
- DAGI Router Audit endpoints
- Node metrics and Guardian/Steward APIs
- check-invariants.py for deploy verification
- node-bootstrap.sh for node self-registration
- node-guardian-loop.py for continuous self-healing
- Updated Node Directory UI with better error handling
- Node Cabinet with metrics cards and DAGI Router card"
# Запушити
git push origin main
```
---
## 🖥️ КРОК 2: На сервері NODE1 (Hetzner)
### 2.1. SSH на сервер
```bash
ssh root@<NODE1_IP>
# або через ваш алиас
ssh node1
```
### 2.2. Перейти в директорію проєкту
```bash
cd /opt/daarion
# або ваш шлях до проєкту
```
### 2.3. Оновити код
```bash
git pull origin main
```
### 2.4. Застосувати міграції
```bash
# Підключитися до PostgreSQL
docker exec -it daarion-postgres psql -U daarion_user -d daarion
# Або напряму через psql
PGPASSWORD=<password> psql -h localhost -U daarion_user -d daarion
# Виконати міграції послідовно:
\i migrations/034_agent_prompts_seed.sql
\i migrations/035_agent_dagi_audit.sql
\i migrations/036_node_metrics_extended.sql
\i migrations/037_node_agents_complete.sql
\i migrations/038_agent_prompts_full_coverage.sql
\i migrations/039_node_registry_self_healing.sql
# Вийти
\q
```
### 2.5. Перебілдити і перезапустити сервіси
```bash
# Зупинити сервіси
docker compose -f docker-compose.all.yml down
# Перебілдити
docker compose -f docker-compose.all.yml build
# Запустити
docker compose -f docker-compose.all.yml up -d
```
### 2.6. Перевірити здоров'я
```bash
# Перевірити статус контейнерів
docker ps | grep daarion
# Перевірити логи city-service
docker logs -f daarion-city-service --tail 100
# Перевірити /healthz
curl http://localhost:7001/healthz
# Перевірити /public/nodes
curl http://localhost:7001/public/nodes | jq
```
---
## 🔍 КРОК 3: Перевірка інваріантів
```bash
# На сервері (або локально якщо є доступ)
python3 scripts/check-invariants.py --base-url http://localhost:7001
# Очікуваний результат:
# ✅ ALL INVARIANTS PASSED
# або
# ⚠️ WARNINGS (деякі можуть бути нормальними)
```
---
## 🧪 КРОК 4: Smoke-тести
```bash
# Якщо встановлено pytest
pytest tests/test_infra_smoke.py -v --base-url http://localhost:7001
```
---
## 🌐 КРОК 5: Перевірка в браузері
1. **Node Directory:** https://daarion.space/nodes
- Повинні відображатися NODE1 і NODE2
- Без "Помилка завантаження нод"
2. **Node Cabinet:** https://daarion.space/nodes/node-1-hetzner-gex44
- Метрики CPU/GPU/RAM/Disk
- DAGI Router Card
- Guardian/Steward агенти
3. **Agents:** https://daarion.space/agents
- System Prompts для агентів
---
## 🔄 КРОК 6 (опційно): Node Bootstrap
Якщо ноди не з'являються після міграції:
```bash
# На NODE1
NODE_ID=node-1-hetzner-gex44 \
NODE_NAME="NODE1 — Hetzner GEX44" \
NODE_ENVIRONMENT=production \
NODE_ROLES=production,gpu,ai_runtime,storage,matrix \
./scripts/node-bootstrap.sh
# На NODE2 (якщо потрібно)
NODE_ID=node-2-macbook-m4max \
NODE_NAME="NODE2 — MacBook Pro M4 Max" \
NODE_ENVIRONMENT=development \
NODE_ROLES=development,gpu,ai_runtime,testing \
./scripts/node-bootstrap.sh
```
---
## ❌ Rollback (якщо щось пішло не так)
```bash
# Відкотити код
git reset --hard HEAD~1
git push -f origin main
# На сервері
git pull origin main
docker compose -f docker-compose.all.yml down
docker compose -f docker-compose.all.yml up -d
```
---
## 📊 Очікуваний результат
Після успішного деплою:
| Компонент | URL | Очікуваний статус |
|-----------|-----|-------------------|
| Health | /healthz | `{"status": "ok"}` |
| Nodes | /public/nodes | `{"items": [...], "total": 2}` |
| Node Cabinet | /nodes/{id} | Метрики + DAGI + Agents |
| Invariants | check-invariants.py | ✅ PASSED |
---
## 🆘 Troubleshooting
### "Failed to fetch nodes"
1. Перевірити логи: `docker logs daarion-city-service`
2. Перевірити чи є записи в node_cache: `SELECT * FROM node_cache;`
3. Застосувати міграцію 039
### "node_registry does not exist"
```sql
\i migrations/039_node_registry_self_healing.sql
```
### "Ноди не відображаються"
```bash
# Перевірити node_cache
docker exec -it daarion-postgres psql -U daarion_user -d daarion -c "SELECT node_id, node_name FROM node_cache;"
# Якщо порожньо — запустити bootstrap
./scripts/node-bootstrap.sh
```

View File

@@ -0,0 +1,214 @@
# TASK_PHASE_AGENT_SYSTEM_PROMPTS_MVP_v1
## Проєкт
microdao-daarion (MVP DAARION.city)
## Статус
**COMPLETED** — 2025-11-30
## Мета
Зробити так, щоб системні промти агентів:
- зберігались у реальній БД (`agent_prompts` таблиця)
- завантажувались через API
- редагувалися через UI на сторінці `/agents/:slug` (вкладка System Prompts)
Після виконання цієї фази вкладка System Prompts перестає бути "плейсхолдером" і працює як повноцінний редактор системних промтів для ключових агентів DAARION.city.
---
## Виконані роботи
### 1. Аналіз проблеми
**Причина порожніх промтів:**
- Backend routes (`routes_city.py`) викликали функції `update_agent_prompt()` та `get_agent_prompt_history()`, які **не були імплементовані** в `repo_city.py`
- Функція `get_agent_prompts()` вже існувала і правильно повертала дані
**Структура, яка вже працювала:**
- ✅ Міграція `016_agent_prompts.sql` — таблиця створена
-`GET /city/agents/{agent_id}/dashboard` — повертає `system_prompts`
- ✅ Frontend компонент `AgentSystemPromptsCard.tsx`
- ✅ Next.js API routes proxy
### 2. Backend: Додані функції в `repo_city.py`
#### `update_agent_prompt(agent_id, kind, content, created_by, note)`
- Деактивує попередню версію промта
- Створює нову версію з інкрементованим номером
- Повертає оновлений запис
#### `get_agent_prompt_history(agent_id, kind, limit)`
- Повертає історію всіх версій промту
- Впорядковано по версії (DESC)
**Файл:** `services/city-service/repo_city.py` (рядки ~628-705)
### 3. Seed Data: Міграція `034_agent_prompts_seed.sql`
Створено детальні системні промти для ключових агентів:
| Агент | Промти | Роль |
|-------|--------|------|
| DAARWIZZ | core, safety, governance | City Mayor / Orchestrator |
| DARIA | core, safety | Technical Support |
| DARIO | core | Community Manager |
| SOUL | core, safety | District Lead (Wellness) |
| Spirit | core | Guidance Agent |
| Logic | core | Information Agent |
| Helion | core, safety, tools | District Lead (Energy) |
| GREENFOOD | core, safety | District Lead (Supply-Chain) |
---
## API Reference
### Отримати всі промти агента
```
GET /city/agents/{agent_id}/dashboard
```
Повертає `system_prompts` об'єкт з 4 типами: core, safety, governance, tools
### Оновити промт
```
PUT /city/agents/{agent_id}/prompts/{kind}
Content-Type: application/json
{
"content": "New prompt content...",
"note": "Optional change note"
}
```
### Отримати історію промту
```
GET /city/agents/{agent_id}/prompts/{kind}/history?limit=10
```
---
## Схема БД: `agent_prompts`
```sql
CREATE TABLE agent_prompts (
id uuid PRIMARY KEY DEFAULT gen_random_uuid(),
agent_id text NOT NULL,
kind text NOT NULL CHECK (kind IN ('core', 'safety', 'governance', 'tools')),
content text NOT NULL,
version integer NOT NULL DEFAULT 1,
created_at timestamptz NOT NULL DEFAULT now(),
created_by text,
note text,
is_active boolean NOT NULL DEFAULT true
);
```
**Індекси:**
- `idx_agent_prompts_agent_kind` — пошук активних промтів
- `idx_agent_prompts_agent_created_at` — сортування по часу
- `idx_agent_prompts_active` — фільтр активних
---
## Frontend
### Сторінка агента
`/agents/[agentId]` → вкладка "System Prompts"
### Компоненти
- `apps/web/src/app/agents/[agentId]/page.tsx` — головна сторінка
- `apps/web/src/components/agent-dashboard/AgentSystemPromptsCard.tsx` — редактор промтів
- `apps/web/src/lib/agent-dashboard.ts` — API клієнт
### Можливості
- Перемикання між типами промтів (core/safety/governance/tools)
- Редагування тексту промта
- Збереження змін з індикацією статусу
- Перегляд версії та часу останнього оновлення
---
## Застосування міграції
```bash
# На сервері
cd /opt/microdao-daarion
psql -U postgres -d daarion < migrations/034_agent_prompts_seed.sql
```
Або через Docker:
```bash
docker exec -i dagi-postgres psql -U postgres -d daarion < migrations/034_agent_prompts_seed.sql
```
---
## Acceptance Criteria
- ✅ Для будь-якого агента з seed-промтами: `/agents/:id` → вкладка System Prompts показує реальний текст з БД
- ✅ Редагування промта з UI: змінює запис у БД, після перезавантаження новий текст відображається
- ✅ API GET/PUT працюють коректно
- ✅ Версіонування: кожне збереження створює нову версію
- ✅ Seed-дані для 8 ключових агентів
---
## Out of Scope (на потім)
- [ ] UI для перегляду історії версій
- [ ] Перемикання на попередню версію (rollback)
- [ ] RBAC перевірки (хто може редагувати)
- [ ] Інтеграція з DAGI Router runtime
---
## Файли змінені/створені
### Змінені
- `services/city-service/repo_city.py` — додані функції update_agent_prompt, get_agent_prompt_history
### Створені
- `migrations/034_agent_prompts_seed.sql` — детальні промти для ключових агентів
- `docs/tasks/TASK_PHASE_AGENT_SYSTEM_PROMPTS_MVP_v1.md` — цей документ
### Вже існували (без змін)
- `migrations/016_agent_prompts.sql` — схема таблиці
- `services/city-service/routes_city.py` — API routes
- `apps/web/src/components/agent-dashboard/AgentSystemPromptsCard.tsx` — UI компонент
- `apps/web/src/lib/agent-dashboard.ts` — API клієнт
- `apps/web/src/app/api/agents/[agentId]/prompts/[kind]/route.ts` — Next.js proxy
---
## Тестування
### Backend (curl)
```bash
# Отримати dashboard з промтами
curl http://localhost:7001/city/agents/AGENT_ID/dashboard | jq '.system_prompts'
# Оновити промт
curl -X PUT http://localhost:7001/city/agents/AGENT_ID/prompts/core \
-H "Content-Type: application/json" \
-d '{"content": "Test prompt", "note": "Test update"}'
# Отримати історію
curl http://localhost:7001/city/agents/AGENT_ID/prompts/core/history
```
### Frontend
1. Відкрити http://localhost:8899/agents
2. Вибрати агента (DAARWIZZ, DARIA, тощо)
3. Перейти на вкладку "System Prompts"
4. Перевірити що відображаються seed-промти
5. Змінити текст та натиснути "Save"
6. Перезавантажити сторінку — зміни збережені
---
**Версія:** 1.0.0
**Дата:** 2025-11-30
**Автор:** DAARION AI Team

View File

@@ -0,0 +1,157 @@
# TASK_PHASE_AGENT_SYSTEM_PROMPTS_MVP_v2
## Проєкт
microdao-daarion (MVP DAARION.city)
## Фаза
Agent System Prompts — Coverage + Runtime Integration
## Статус
**COMPLETED**
---
## Мета
1. Заповнити системні промти для всіх ключових агентів міста (City / District / Node)
2. Підключити зберігання промтів у БД до реального DAGI Router runtime
---
## Результат
### 1. Повне покриття агентів (16 агентів)
#### City / Core
-**DAARWIZZ** — core, safety, governance, tools
-**MicroDAO Orchestrator** — core, safety
-**DevTools Agent** — core, safety, tools
#### District / MicroDAO
-**GREENFOOD** — core, safety, tools
-**Helion** — core, safety, tools
-**SOUL** — core, safety
-**DRUID** — core, safety, tools
-**NUTRA** — core, safety
-**EONARCH** — core, safety
-**CLAN** — core
-**Yaromir** — core
-**Monitor** — core, safety
#### Node Agents
-**monitor-node1** (Node Guardian NODE1) — core, safety, governance
-**monitor-node2** (Node Guardian NODE2) — core, safety
-**node-steward-node1** — core
-**node-steward-node2** — core
### 2. Runtime Integration
#### Нові API Endpoints
```
GET /internal/agents/{agent_id}/prompts/runtime
```
Повертає промти для агента (тільки content, без метаданих).
```
GET /internal/agents/{agent_id}/system-prompt
```
Повертає зібраний system prompt для LLM виклику.
```
POST /internal/agents/prompts/status
Body: { "agent_ids": ["agent-1", "agent-2"] }
```
Перевіряє наявність промтів для списку агентів.
#### DAGI Router Integration
Створено `services/router/prompt_builder.py`:
- `PromptBuilder` клас для побудови system prompts
- Пріоритети: БД → router-config → fallback
- Автоматичне завантаження контексту (node, district)
- `get_agent_system_prompt()` convenience function
Оновлено `/v1/agents/{agent_id}/infer`:
- Автоматично завантажує system prompt з БД
- Fallback на router-config.yml
- Логування джерела промту
### 3. UI Індикатори
#### DAGIRouterCard
- 🧠 іконка біля імені агента якщо `has_prompts = true`
- Напівпрозора іконка якщо агент active але без промтів
- Tooltip з інформацією про статус
### 4. Файли
#### Міграції
- `migrations/038_agent_prompts_full_coverage.sql` — повний seed
#### Backend
- `services/city-service/repo_city.py`:
- `get_runtime_prompts(agent_id)`
- `build_system_prompt(agent, prompts, context)`
- `get_agent_with_runtime_prompt(agent_id)`
- `check_agents_prompts_status(agent_ids)`
- `services/city-service/routes_city.py`:
- Нові endpoints для runtime prompts
- `DAGIRouterAgentItem.has_prompts` поле
#### Router
- `services/router/prompt_builder.py` — новий модуль
- `services/router/main.py` — інтеграція з prompt_builder
#### Frontend
- `apps/web/src/hooks/useDAGIAudit.ts``has_prompts` в типах
- `apps/web/src/components/node-dashboard/DAGIRouterCard.tsx` — UI індикатор
#### Тести
- `tests/test_agent_prompts_runtime.py`
---
## Acceptance Criteria
| Критерій | Статус |
|----------|--------|
| Всі агенти з Target Coverage мають core prompt | ✅ |
| DAGI Router завантажує промти з БД | ✅ |
| Fallback на config якщо БД порожня | ✅ |
| UI показує індикатор has_prompts | ✅ |
| API для batch перевірки статусу | ✅ |
| Unit тести | ✅ |
---
## Як застосувати
```bash
# 1. Застосувати міграцію
docker exec -i dagi-postgres psql -U postgres -d daarion < migrations/038_agent_prompts_full_coverage.sql
# 2. Перезапустити city-service
docker-compose restart daarion-city-service
# 3. Перезапустити router (опційно)
docker-compose restart daarion-router
# 4. Зібрати frontend
cd apps/web && npm run build
# 5. Запустити тести
pytest tests/test_agent_prompts_runtime.py -v
```
---
## Наступні кроки (v3)
1. **Версіонування промтів** — історія змін з rollback
2. **A/B testing** — різні версії промтів для тестування
3. **Template system** — шаблони з variables
4. **Metrics** — трекінг ефективності промтів
5. **UI Editor** — advanced editor з preview

View File

@@ -0,0 +1,296 @@
# TASK_PHASE_DAGI_AGENT_AUDIT_MVP_v1
Проєкт: DAARION.city — DAGI Router / Node Cabinet
Фаза: DAGI Agent Audit & Activity Monitor
Мета: гарантувати, що всі агенти, оголошені DAGI Router на кожній Ноді (NODA1, NODA2), коректно синхронізовані з системою microdao та відображаються у Кабінеті Ноди з правильним індикатором активності.
---
# 0. Problem Statement
У процесі розробки та деплою деякі агенти на НОДА з'являлись/зникали.
Не було механізму перевірки їх присутності та активності у DAGI Router та їх відповідності записам у системі (microdao → agents).
Потрібно створити:
- одноразовий аудит DAGI-агентів на кожній ноді;
- постійний автоматизований моніторинг активності агентів;
- індикатор «підключено/активний» замість терміну «зареєстрований у MVP»;
- UI-відображення в Кабінеті Ноди;
- метрики й сигналізація (NATS + Prometheus).
---
# 1. Scope
## Включено
- Аудит DAGI Router агентів на NODA1 та NODA2.
- Зіставлення: `router_agents``system_agents` (таблиця microdao.agents).
- Додавання індикатора активності агента.
- Одноразовий звіт diff у JSON.
- Автоматичний воркер для періодичної перевірки.
- Метрики Prometheus.
- Події NATS.
- UI (Node Cabinet → вкладка "DAGI Router").
## Виключено
- Вплив на логіку DAGI Router.
- Автоматичне видалення агентів.
- Версіонування агентів.
---
# 2. Definitions
- **Router Agents** — агенти, які DAGI Router бачить на конкретній ноді (`GET /api/agents` або NATS `dagi.router.agent.list`).
- **System Agents** — агенти, зареєстровані в системі (таблиця `agents` у microdao).
- **Node Agent Auditor** — спеціальний агент Ноди, який періодично перевіряє відповідність.
- **Active** — агент з'являється в DAGI Router і відповідає на healthcheck.
- **Stale** — агент є в системі, але його немає в DAGI Router.
- **Phantom** — агент є в DAGI Router, але його немає в системі.
---
# 3. One-time Audit (Node1 + Node2)
## 3.1. Команда
Створити CLI/скрипт:
```bash
scripts/dagi_agent_audit.py --node node1
scripts/dagi_agent_audit.py --node node2
```
## 3.2. Дії
1. Отримати список агентів з DAGI Router:
```
GET {ROUTER_URL}/api/agents
```
2. Отримати список агентів з microdao:
```sql
SELECT id, name, role, node_id FROM agents WHERE node_id = :node
```
3. Обчислити:
```python
missing_in_system = router_ids - system_ids
stale_in_router = system_ids - router_ids
active = intersection(router_ids, system_ids)
```
4. Згенерувати звіт:
```
logs/dagi-audit-node{1,2}.json
```
## 3.3. Структура JSON-звіту
```json
{
"node_id": "node1",
"router_total": 15,
"system_total": 14,
"active": ["agent_x", "agent_y"],
"missing_in_system": ["agent_z"],
"stale_in_router": ["agent_a"],
"timestamp": "..."
}
```
---
# 4. DB / System Changes
## 4.1. Таблиця agents (розширення)
Додати поля:
- `node_id text` — ідентифікатор ноди.
- `status text check(status in ('active','stale','missing','error'))` — стан.
- `last_seen_at timestamptz` — останній час успішного контакту.
Міграція:
```sql
ALTER TABLE agents ADD COLUMN IF NOT EXISTS node_id text;
ALTER TABLE agents ADD COLUMN IF NOT EXISTS status text DEFAULT 'stale';
ALTER TABLE agents ADD COLUMN IF NOT EXISTS last_seen_at timestamptz;
```
## 4.2. Repo-методи
- `repo_agents.update_status(agent_id, status, last_seen_at)`
- `repo_agents.list_by_node(node_id)`
- `repo_agents.sync_router_list(node_id, router_agents)` — optional
---
# 5. Automated Worker: Node Agent Auditor
Створити сервіс:
`services/node-agent-auditor/worker.py`
## 5.1. Частота
- кожні 60 секунд (конфігуровано).
## 5.2. Алгоритм
```python
router_agents = get_router_list(node)
system_agents = get_system_list(node)
active = intersection(router_agents, system_agents)
missing = router_agents - system_agents
stale = system_agents - router_agents
update agents.status
update agents.last_seen_at
publish NATS events
expose Prometheus metrics
```
## 5.3. NATS події
- `node.agent.audit.active`
- `node.agent.audit.missing`
- `node.agent.audit.stale`
- `node.agent.audit.error`
Payload:
```json
{
"node_id": "node1",
"agent_id": "daria",
"status": "missing",
"timestamp": "..."
}
```
## 5.4. Prometheus метрики
- `dagi_agents_active{node="node1"}`
- `dagi_agents_missing{node="node1"}`
- `dagi_agents_stale{node="node1"}`
- `dagi_agent_last_seen_timestamp{agent="daria",node="node1"}`
---
# 6. Node Cabinet UI
## 6.1. Нова вкладка
```
/node/{nodeId}/dagi-router
```
## 6.2. Таблиця
Колонки:
- Agent ID
- Name
- Role
- Status (`active`, `missing`, `stale`, `error`)
- Last Seen (`timestamp`)
- Node
## 6.3. Індикатор статусу
- 🟢 Зелене коло — active
- 🟡 Жовте — stale
- 🔴 Червоне — missing
-Сіре — error
## 6.4. Елементи управління
- `Resync` → тригерить ручний аудит (POST `/internal/node/{id}/audit`).
---
# 7. API
## 7.1. GET
- `GET /internal/node/{node_id}/agents/router` → список DAGI Router агентів
- `GET /internal/node/{node_id}/agents/system` → список system agent records
- `GET /internal/node/{node_id}/audit` → останній аудит
## 7.2. POST
- `POST /internal/node/{node_id}/audit` → виконати аудит вручну
- `POST /internal/node/{node_id}/sync` → синхронізувати статуси (опційно)
---
# 8. Tests
## 8.1. Unit
- зіставлення router/system списків
- статуси: active/missing/stale/error
## 8.2. Integration
- worker → DB update
- worker → NATS event
- worker → Prometheus export
## 8.3. E2E
- запуск аудиту
- відображення у Node Cabinet UI
- Resync працює
---
# 9. Acceptance Criteria
- На NODA1 і NODA2 виконано успішний одноразовий аудит.
- JSON-звіти створені.
- Worker працює і оновлює статуси агентів у БД.
- Статуси в UI відповідають реальному стану Router.
- NATS і Prometheus показують коректні дані.
- Resync викликає миттєве оновлення.
---
# 10. Deliverables
- `scripts/dagi_agent_audit.py`
- `services/node-agent-auditor/worker.py`
- Міграція agents.status/last_seen_at/node_id
- API (internal)
- Node Cabinet UI вкладка
- Документація цього таску
---
# 11. Implementation Plan
## M0 — Одноразовий аудит (Day 1)
1. Створити `scripts/dagi_agent_audit.py`
2. Тест на NODA1 та NODA2
3. Звіти в `logs/`
## M1 — DB + Repo (Day 1-2)
1. Міграція для нових полів
2. Repo-методи в city-service
## M2 — Worker (Day 2-3)
1. Node Agent Auditor сервіс
2. NATS integration
3. Prometheus metrics
## M3 — UI (Day 3-4)
1. Node Cabinet вкладка "DAGI Router"
2. Таблиця агентів зі статусами
3. Resync button
---
**Версія:** 1.0.0
**Дата:** 2025-11-30
**Статус:** READY FOR IMPLEMENTATION

View File

@@ -0,0 +1,179 @@
# TASK_PHASE_DAGI_AGENT_AUTOSYNC_AND_METRICS_v1
## Проєкт
DAARION.city — Node Cabinet / DAGI Router
## Мета
Створити стабільний, ергономічний та самовідновлюваний кабінет Ноди, де:
- DAGI-агенти кожної ноди відображаються в таблиці на вкладці "DAGI Router"
- Статуси агентів (Active / Phantom / Stale / Error) автоматично синхронізуються
- GPU/CPU/RAM/Disks та кількість агентів стабільно відображаються
- Є набір API тестів для захисту від регресій
---
## Зроблено
### 1. Database Migration (036)
**Файл:** `migrations/036_node_metrics_extended.sql`
Розширено `node_cache` полями:
- CPU: `cpu_model`, `cpu_cores`, `cpu_usage`
- GPU: `gpu_model`, `gpu_vram_total`, `gpu_vram_used`
- RAM: `ram_total`, `ram_used`
- Disk: `disk_total`, `disk_used`
- Agents: `agent_count_router`, `agent_count_system`
- Heartbeat: `last_heartbeat`, `dagi_router_url`
Початкові дані для NODE1 (Hetzner) та NODE2 (MacBook M4 Max).
### 2. Backend API Endpoints
**Файли:**
- `services/city-service/repo_city.py` — repo методи
- `services/city-service/routes_city.py` — FastAPI endpoints
#### Нові endpoints:
| Endpoint | Метод | Опис |
|----------|-------|------|
| `/internal/node/{node_id}/dagi-router/agents` | GET | Таблиця агентів для Node Cabinet |
| `/internal/node/{node_id}/metrics/current` | GET | Метрики ноди (GPU/CPU/RAM/Disk) |
| `/internal/node/{node_id}/metrics/update` | POST | Оновлення метрик (heartbeat) |
| `/internal/node/{node_id}/dagi-router/phantom/sync` | POST | Синхронізація phantom агентів |
| `/internal/node/{node_id}/dagi-router/stale/mark` | POST | Позначення stale агентів |
#### Response structures:
**GET /dagi-router/agents:**
```json
{
"node_id": "node-2-macbook-m4max",
"last_audit_at": "2025-11-30T14:35:00Z",
"summary": {
"active": 12,
"phantom": 2,
"stale": 5,
"router_total": 14,
"system_total": 17
},
"agents": [
{
"id": "daria",
"name": "DARIA",
"role": "city_guide",
"status": "active",
"node_id": "node-2-macbook-m4max",
"models": [],
"gpu": "Apple M4 Max GPU",
"cpu": "16 cores",
"last_seen_at": "2025-11-30T14:34:50Z",
"has_cabinet": true,
"cabinet_slug": "daria"
}
]
}
```
**GET /metrics/current:**
```json
{
"node_id": "node-2-macbook-m4max",
"node_name": "MacBook Pro M4 Max",
"cpu_model": "Apple M4 Max",
"cpu_cores": 16,
"cpu_usage": 35.5,
"gpu_model": "Apple M4 Max GPU",
"gpu_memory_total": 40960,
"gpu_memory_used": 28000,
"ram_total": 65536,
"ram_used": 40000,
"disk_total": 1024000,
"disk_used": 400000,
"agent_count_router": 14,
"agent_count_system": 17,
"last_heartbeat": "2025-11-30T05:14:59Z"
}
```
### 3. Frontend Components
**Нові/оновлені файли:**
- `apps/web/src/hooks/useDAGIAudit.ts` — хуки для API
- `apps/web/src/components/node-dashboard/DAGIRouterCard.tsx` — таблиця агентів
- `apps/web/src/components/node-dashboard/NodeMetricsCard.tsx` — метрики ноди
- `apps/web/src/app/nodes/[nodeId]/page.tsx` — інтеграція в Node Cabinet
#### DAGIRouterCard Features:
- Таблиця агентів з колонками: Agent, Status, Runtime, Last Seen, Cabinet
- Фільтр по статусу (All / Active / Phantom / Stale)
- Пошук по імені агента
- Кнопка "Запустити аудит"
- Кнопка "Sync" для phantom агентів
- Лічильники Active/Phantom/Stale
#### NodeMetricsCard Features:
- Progress bars для GPU/CPU/RAM/Disk
- Показує модель GPU/CPU
- Agent counts (Router / System)
- Last heartbeat timestamp
### 4. API Tests
**Файл:** `tests/test_dagi_router_api.py`
Тести для:
- `TestDAGIRouterAgents` — GET agents endpoint
- `TestNodeMetrics` — GET metrics endpoint
- `TestDAGIAudit` — POST audit endpoint
- `TestPhantomStaleSync` — sync endpoints
- `TestIntegration` — повний цикл
---
## Застосування на сервері
```bash
# 1. Застосувати міграцію
docker exec -i dagi-postgres psql -U postgres -d daarion < migrations/036_node_metrics_extended.sql
# 2. Перезапустити city-service
docker-compose restart daarion-city-service
# 3. Зібрати frontend
cd apps/web && npm run build
# 4. Запустити тести
cd /opt/microdao-daarion
pytest tests/test_dagi_router_api.py -v
```
---
## Acceptance Criteria
- [x] API `/dagi-router/agents` повертає уніфіковану таблицю агентів
- [x] API `/metrics/current` повертає метрики ноди
- [x] Node Cabinet показує NodeMetricsCard з GPU/CPU/RAM/Disk
- [x] Node Cabinet показує DAGIRouterCard з таблицею агентів
- [x] Phantom агенти можна синхронізувати через UI
- [x] Stale агенти відображаються окремо
- [x] API тести покривають основні сценарії
- [x] Обидві ноди (NODE1, NODE2) працюють однаково
---
## Залежності
- Migration 035 (`agent_prompts_seed.sql`)
- Migration 022 (`node_cache` table)
- Migration 030 (`guardian_agent_id`, `steward_agent_id`)
---
## Наступні кроки
1. Інтегрувати heartbeat agent на нодах для оновлення метрик
2. Додати Grafana dashboard для візуалізації метрик
3. Реалізувати автоматичний periodic audit (cron job)

View File

@@ -0,0 +1,214 @@
# TASK_PHASE_INFRA_INVARIANTS_AND_DEPLOY_CHECKS_v1
## Проєкт
DAARION.city — Infra / Deploy / DAGI / microdao
## Фаза
Інваріанти інфраструктури + автоматичні перевірки після деплою
## Статус
**COMPLETED**
---
## Мета
Зробити деплой детермінованим і безпечним так, щоб базова логіка **Нода → DAGI Router → Агенти → microdao → System Prompts** не ламалася після оновлень.
---
## Problem Statement
### Симптоми
- Після оновлень/деплоїв періодично:
- зникають агенти у Кабінеті Ноди (0 agents total)
- ламаються кабінети агентів (`404`, відсутні `public_slug`)
- зникають метрики GPU/CPU/RAM/Disk
- DAGI Router / microdao втрачають частину зв'язків
### Причина
- Немає **формально зафіксованих інваріантів**, які перевіряються автоматично після кожного деплою
- Деплой проходить навіть тоді, коли стан системи неконсистентний
---
## Рішення
### 1. Інваріанти зафіксовані в коді
Файл: `scripts/check-invariants.py`
#### Node Invariants
| Node | Інваріант | Severity |
|------|-----------|----------|
| NODE1, NODE2 | Існує в `node_cache` | CRITICAL |
| NODE1, NODE2 | `agent_count_router >= 1` | CRITICAL |
| NODE1, NODE2 | `agent_count_system >= 1` | CRITICAL |
| NODE1 | GPU configured | WARNING |
| NODE1, NODE2 | Heartbeat < 10 min | WARNING |
#### Node Agents Invariants
| Інваріант | Severity |
|-----------|----------|
| Node Guardian exists | CRITICAL |
| Node Steward exists | CRITICAL |
| Total agents >= 1 | CRITICAL |
#### DAGI Router Invariants
| Інваріант | Severity |
|-----------|----------|
| `router_total >= 1` | WARNING |
| `phantom_count <= 20` | WARNING |
| `stale_count <= 20` | WARNING |
#### Core Agents Invariants
| Agent | Required | Severity |
|-------|----------|----------|
| DAARWIZZ | core prompt | WARNING |
| MicroDAO Orchestrator | core prompt | WARNING |
| DevTools | core prompt | WARNING |
| SOUL | core prompt | WARNING |
| GREENFOOD | core prompt | WARNING |
| Helion | core prompt | WARNING |
| DRUID | core prompt | WARNING |
| NUTRA | core prompt | WARNING |
| Monitor | core prompt | WARNING |
### 2. Скрипт перевірки
```bash
# Запуск перевірки
python scripts/check-invariants.py --base-url http://localhost:7001
# Перевірка тільки NODE1
python scripts/check-invariants.py --node node-1-hetzner-gex44
# JSON output
python scripts/check-invariants.py --json
```
#### Exit codes
- `0` — всі критичні інваріанти пройшли
- `1` — є критичні помилки
### 3. Smoke Tests
Файл: `tests/test_infra_smoke.py`
```bash
# Запуск тестів
pytest tests/test_infra_smoke.py -v
# З custom URL
pytest tests/test_infra_smoke.py -v --base-url http://localhost:7001
```
#### Тести
- `TestHealthChecks``/healthz`, `/public/nodes`
- `TestNodeMetrics` — метрики нод, agent counts
- `TestNodeAgents` — Guardian, Steward
- `TestDAGIRouter` — DAGI agents, summary
- `TestCoreAgents` — prompts status, runtime prompts
- `TestIntegration` — end-to-end flows
### 4. Інтеграція в Deploy
Файл: `scripts/deploy-prod.sh`
```bash
# Деплой з автоматичною перевіркою інваріантів
./scripts/deploy-prod.sh
# Деплой зі smoke тестами
RUN_SMOKE_TESTS=true ./scripts/deploy-prod.sh
```
#### Pipeline
1. Pre-flight checks (Docker, .env, compose files)
2. Database backup
3. Pull/build images
4. Start core services
5. Run migrations
6. Start all services
7. Basic health checks
8. **Infrastructure invariants check** ← NEW
9. (Optional) Smoke tests
10. Success/failure report
---
## Файли
| Файл | Опис |
|------|------|
| `scripts/check-invariants.py` | CLI для перевірки інваріантів |
| `tests/test_infra_smoke.py` | Pytest smoke тести |
| `scripts/deploy-prod.sh` | Оновлений deploy script |
---
## Використання
### Щоденна розробка
```bash
# Перевірити інваріанти вручну
python scripts/check-invariants.py --base-url http://localhost:7001
# Запустити smoke тести
pytest tests/test_infra_smoke.py -v
```
### Production Deploy
```bash
# Повний деплой з інваріантами
./scripts/deploy-prod.sh
# Якщо інваріанти не пройшли:
# 1. Перевірити міграції
psql -h localhost -U postgres -d daarion < migrations/037_node_agents_complete.sql
psql -h localhost -U postgres -d daarion < migrations/038_agent_prompts_full_coverage.sql
# 2. Перезапустити перевірку
python scripts/check-invariants.py
```
### CI/CD Integration
```yaml
# GitHub Actions example
- name: Deploy
run: ./scripts/deploy-prod.sh
- name: Check Invariants
run: python scripts/check-invariants.py --base-url ${{ secrets.CITY_SERVICE_URL }}
- name: Run Smoke Tests
run: pytest tests/test_infra_smoke.py -v
```
---
## Acceptance Criteria
| Критерій | Статус |
|----------|--------|
| `scripts/check-invariants.py` існує і працює | ✅ |
| Перевіряє NODE1 та NODE2 | ✅ |
| Перевіряє Node Guardian/Steward | ✅ |
| Перевіряє DAGI Router | ✅ |
| Перевіряє core agents prompts | ✅ |
| Exit code 1 при критичних помилках | ✅ |
| Інтегровано в deploy-prod.sh | ✅ |
| Smoke тести в pytest | ✅ |
---
## Наступні кроки
1. **Prometheus metrics** для інваріантів
2. **Alerting** при порушенні інваріантів
3. **GitHub Actions** CI/CD pipeline
4. **Rollback automation** при failed invariants

View File

@@ -0,0 +1,142 @@
# TASK_PHASE_NODE_AGENT_CABINETS_INTEGRATION_v1
## Проєкт
DAARION.city — Node Cabinet / Agents / DAGI Router
## Мета
Зробити єдиний, послідовний шар відображення агентів ноди:
- DAGI Router → показує фактичних агентів ноди
- Кабінет Ноди → показує тих самих агентів у секціях "Node Guardian & Steward"
- Кабінет Агента (`/agents/:slug`) + System Prompts працюють для всіх активних агентів
---
## Виконано
### 1. Database Migration (037)
**Файл:** `migrations/037_node_agents_complete.sql`
Створено/оновлено:
- **Node Guardian** агентів для NODE1 та NODE2
- **Node Steward** агентів для NODE1 та NODE2
- Прив'язки `guardian_agent_id` та `steward_agent_id` в `node_cache`
- **System Prompts** для всіх Node Agents
- Синхронізація ключових агентів з `router-config.yml`
### 2. Backend API
**Новий endpoint:**
`GET /internal/node/{node_id}/agents`
```json
{
"node_id": "node-2-macbook-m4max",
"total": 4,
"guardian": {
"id": "monitor-node2",
"name": "Node Guardian (НОДА2)",
"slug": "monitor-node2",
"kind": "node_guardian",
"status": "online",
"is_guardian": true
},
"steward": {
"id": "node-steward-node2",
"name": "Node Steward (НОДА2)",
"slug": "node-steward-node2",
"kind": "node_steward",
"status": "online",
"is_steward": true
},
"agents": [...]
}
```
**Оновлення:**
- `repo_city.get_agent_by_id()` — тепер шукає по `id` АБО `public_slug`
- `repo_city.get_node_agents()` — новий метод для отримання агентів ноди
### 3. Frontend
**Оновлені файли:**
- `apps/web/src/hooks/useDAGIAudit.ts` — додано `useNodeAgents` hook
- `apps/web/src/app/nodes/[nodeId]/page.tsx` — інтеграція з useNodeAgents
- `apps/web/src/components/nodes/NodeGuardianCard.tsx` — посилання на `/agents/{slug}`
**Зміни:**
- NodeGuardianCard використовує `slug` для посилань замість `id`
- Node Cabinet отримує Guardian/Steward через новий API
- Fallback на nodeProfile якщо API не повернув дані
### 4. Node Agents Seed Data
| Agent | Node | Kind | Slug |
|-------|------|------|------|
| Node Guardian (НОДА1) | node-1-hetzner-gex44 | node_guardian | monitor-node1 |
| Node Guardian (НОДА2) | node-2-macbook-m4max | node_guardian | monitor-node2 |
| Node Steward (НОДА1) | node-1-hetzner-gex44 | node_steward | node-steward-node1 |
| Node Steward (НОДА2) | node-2-macbook-m4max | node_steward | node-steward-node2 |
### 5. System Prompts для Node Agents
- **NODE1 Guardian** — core + safety prompts
- **NODE2 Guardian** — core prompt
- **NODE1 Steward** — core prompt
- **NODE2 Steward** — core prompt
---
## Застосування на сервері
```bash
# 1. Застосувати міграцію
docker exec -i dagi-postgres psql -U postgres -d daarion < migrations/037_node_agents_complete.sql
# 2. Перезапустити city-service
docker-compose restart daarion-city-service
# 3. Зібрати frontend
cd apps/web && npm run build
```
---
## Перевірка
```bash
# 1. Перевірити Node Agents API
curl http://localhost:7001/city/internal/node/node-2-macbook-m4max/agents | jq
# 2. Перевірити що агенти мають public_slug
psql -U postgres -d daarion -c "SELECT id, display_name, public_slug, kind FROM agents WHERE kind LIKE 'node_%'"
# 3. Перевірити agent dashboard API
curl http://localhost:7001/city/agents/monitor-node2/dashboard | jq '.profile.display_name'
```
---
## Результат
Після застосування:
1. **Node Cabinet** (`/nodes/[nodeId]`):
- Секція "Node Guardian & Steward" показує реальних агентів
- Кнопки "Кабінет" ведуть на робочі сторінки `/agents/[slug]`
2. **Agent Cabinet** (`/agents/[slug]`):
- Працює для Node Guardian та Node Steward
- System Prompts заповнені
3. **DAGI Router Card**:
- Active агенти мають робочі посилання в Кабінет
- Phantom агенти можна синхронізувати
---
## Залежності
- Migration 036 (node_metrics_extended)
- Migration 035 (agent_dagi_audit)
- Migration 030 (node_guardian_steward)

View File

@@ -0,0 +1,268 @@
# TASK_PHASE_NODE_SELF_HEALING_v1
## Проєкт
DAARION.city — Nodes / Node Cabinet / DAGI Router
## Фаза
Self-healing нод (автоматична реєстрація, відновлення та синхронізація)
## Статус
**COMPLETED**
---
## Мета
Зробити так, щоб:
1. Ноди **ніколи не "зникали"** з Node Directory, якщо фізично існують і шлють heartbeat
2. Реєстрація/оновлення нод виконувалась **агентами ноди**, а не ручними діями
3. Node Directory → Node Cabinet → Node Metrics → DAGI Router були повністю узгоджені
---
## Problem Statement
### Симптом
- `/nodes` (Node Directory) показує:
- «Знайдено нод: 0»
- «Помилка завантаження нод»
- Хоча:
- насправді NODE1/NODE2 є в `node_cache`
- метрики, DAGI Router, агенти ноди працюють
### Причини
- Node Directory фронт дивився на іншу структуру даних
- Реєстрація ноди не відпрацьовувала після деплою
- Немає самовідновлюваної логіки на рівні нод
---
## Рішення
### 1. Node Registry — єдине джерело істини
**Таблиця:** `node_registry`
```sql
CREATE TABLE node_registry (
id text PRIMARY KEY, -- node_id
name text NOT NULL, -- Людська назва
hostname text, -- Hostname
environment text NOT NULL, -- production/development/staging
roles text[] NOT NULL DEFAULT '{}', -- ['gpu', 'ai_runtime', ...]
description text,
is_active boolean NOT NULL DEFAULT true,
registered_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now(),
last_self_registration timestamptz, -- Остання самореєстрація
self_registration_count integer DEFAULT 0
);
```
**View для Node Directory:**
```sql
CREATE VIEW v_nodes_directory AS
SELECT
r.*,
c.cpu_model, c.gpu_model, c.ram_total, ...
c.last_heartbeat,
c.agent_count_router,
c.agent_count_system,
CASE
WHEN c.last_heartbeat < NOW() - INTERVAL '10 minutes' THEN 'stale'
ELSE 'online'
END AS connection_status
FROM node_registry r
LEFT JOIN node_cache c ON c.node_id = r.id
WHERE r.is_active = true;
```
### 2. Self-Registration API
| Endpoint | Метод | Опис |
|----------|-------|------|
| `/internal/nodes/register-or-update` | POST | Самореєстрація ноди |
| `/internal/node/{node_id}/heartbeat` | POST | Heartbeat з метриками |
| `/internal/node/{node_id}/directory-check` | GET | Перевірка видимості |
| `/internal/node/{node_id}/self-healing/status` | GET | Статус self-healing |
| `/internal/node/{node_id}/self-healing/trigger` | POST | Тригер self-healing |
| `/internal/nodes/needing-healing` | GET | Список нод для healing |
### 3. Node Bootstrap Script
**Файл:** `scripts/node-bootstrap.sh`
```bash
# Використання при старті ноди
NODE_ID=node-2-macbook-m4max \
NODE_NAME="MacBook Pro M4 Max" \
NODE_ENVIRONMENT=development \
NODE_ROLES=gpu,ai_runtime,development \
./scripts/node-bootstrap.sh
```
**Що робить:**
1. Відправляє POST на `/internal/nodes/register-or-update`
2. При успіху — відправляє початковий heartbeat
3. При помилці — retry до 5 разів
### 4. Node Guardian Self-Healing Loop
**Файл:** `scripts/node-guardian-loop.py`
```bash
# Запуск як фоновий процес
NODE_ID=node-2-macbook-m4max \
NODE_NAME="NODE2" \
python scripts/node-guardian-loop.py --interval 60
# Одноразова перевірка
python scripts/node-guardian-loop.py --node-id node-2-macbook-m4max --once
```
**Що перевіряє:**
1. Чи нода видима в Node Directory
2. Чи є heartbeat
3. Чи є Guardian/Steward агенти
4. Чи є агенти в router
**Self-healing дії:**
1. Якщо не видима — виконує self-registration
2. Якщо heartbeat старий — відправляє новий
3. Якщо статус error — тригерить healing через API
---
## Файли
| Файл | Опис |
|------|------|
| `migrations/039_node_registry_self_healing.sql` | Міграція для node_registry |
| `services/city-service/repo_city.py` | Функції для self-healing |
| `services/city-service/routes_city.py` | API endpoints |
| `scripts/node-bootstrap.sh` | Bootstrap скрипт |
| `scripts/node-guardian-loop.py` | Self-healing loop |
---
## Інваріанти Self-Healing
| Умова | Дія |
|-------|-----|
| Нода не в node_registry | → self_register() |
| heartbeat > 10 хв | → send_heartbeat() |
| agent_count_router = 0 | → alert + try reinstall |
| guardian_agent_id = NULL | → alert |
| self_healing_status = error | → trigger_healing() |
---
## Використання
### При першому деплої ноди
```bash
# 1. Запустити міграцію
psql -d daarion < migrations/039_node_registry_self_healing.sql
# 2. Запустити bootstrap
NODE_ID=node-2-macbook-m4max \
NODE_NAME="MacBook Pro M4 Max" \
NODE_ENVIRONMENT=development \
./scripts/node-bootstrap.sh
```
### Запуск Guardian Loop
```bash
# Через systemd
[Unit]
Description=DAARION Node Guardian
After=network.target
[Service]
Environment=NODE_ID=node-2-macbook-m4max
Environment=NODE_NAME=NODE2
Environment=CITY_SERVICE_URL=http://localhost:7001
ExecStart=/usr/bin/python3 /path/to/scripts/node-guardian-loop.py
Restart=always
[Install]
WantedBy=multi-user.target
```
### Через Docker Compose
```yaml
services:
node-guardian:
image: python:3.11-slim
environment:
- NODE_ID=node-2-macbook-m4max
- NODE_NAME=NODE2
- CITY_SERVICE_URL=http://city-service:7001
command: python /app/scripts/node-guardian-loop.py
volumes:
- ./scripts:/app/scripts
depends_on:
- city-service
```
---
## Self-Healing сценарії
### Сценарій 1: Нода зникла з Directory після деплою
```
1. Node Guardian запускається
2. check_visibility() → false
3. self_register() → успіх
4. check_visibility() → true
5. ✅ Нода знову в Directory
```
### Сценарій 2: Heartbeat застарів
```
1. Node Guardian перевіряє статус
2. self_healing_status = "stale_heartbeat"
3. send_heartbeat() → успіх
4. ✅ Heartbeat оновлено
```
### Сценарій 3: Agent count = 0
```
1. Node Guardian бачить agent_count_router = 0
2. Логує попередження
3. (Опційно) trigger_healing() для перевірки DAGI Router
4. ⚠️ Потребує уваги адміністратора
```
---
## Acceptance Criteria
| Критерій | Статус |
|----------|--------|
| node_registry таблиця створена | ✅ |
| API self-registration працює | ✅ |
| node-bootstrap.sh виконує реєстрацію | ✅ |
| node-guardian-loop.py запускається | ✅ |
| Ноди видимі в /nodes після реєстрації | ✅ |
| Self-healing при зникненні | ✅ |
| Heartbeat оновлює статус | ✅ |
---
## Наступні кроки
1. **Автоматичний DAGI Router reinstall** при `agent_count_router = 0`
2. **NATS events** для node healing (`node.selfhealing.*`)
3. **Prometheus metrics** для self-healing
4. **Alert rules** для критичних станів
5. **Node Federation** — з'єднання нод між собою