feat(node2): Complete NODE2 setup - guardian, agents, swapper models

- Node-guardian running on MacBook and updating metrics
- NODE2 agents (Atlas, Greeter, Oracle, Builder Bot) assigned to node-2-macbook-m4max
- Swapper models displaying correctly (8 models)
- DAGI Router agents showing with correct status (3 active, 1 stale)
- Router health check using node_cache for remote nodes
This commit is contained in:
Apple
2025-12-02 07:07:58 -08:00
parent 240ceba2e8
commit fca48b3eb0
241 changed files with 2316 additions and 63 deletions

View File

@@ -0,0 +1,478 @@
# TASK_PHASE_MICRODAO_DASHBOARD_v1
Статус: PLANNED
Пріоритет: High (презентація MVP)
## 1. Ціль
Зробити повноцінний **кабінет MicroDAO** (спочатку для `DAARION DAO`), який показує:
- базові метрики DAO (кімнати, громадяни, агенти);
- стрічку активності (новини/апдейти);
- кімнати DAO + кнопки створення кімнати;
- команду DAO (ключові агенти/громадяни);
- підготовлені секції під проєкти/задачі/файли/інтеграції.
Архітектурна вимога: **цей самий дашборд** повинен працювати для будь-якого MicroDAO (`slug`-залежний), щоб його можна було фрактально клонувати.
---
## 2. Поточний стан
Є:
- /microdao — список MicroDAO (з лого, тегами, pinned-сортуванням).
- /microdao/[slug] — сторінка MicroDAO з:
- Branding (лого, банер),
- visibility (public / platform),
- MicroDAO Rooms section,
- базовою статистикою (rooms count).
- /city — кімнати міста (мапа + список).
- /citizens — публічні агенти (громадяни).
- /agents — Agent Console (63 агентів, фільтри по нодах, кнопка "Новий агент").
- API для кімнат і агентів уже працює.
Немає:
- окремого **Dashboard API** для MicroDAO;
- таблиць для **активності (новин)** и **DAO-level метрик**;
- явної прив'язки громадян до конкретного MicroDAO (лише district/room mapping).
---
## 3. Архітектура рішення
### 3.1. База даних (PostgreSQL)
#### 3.1.1. Таблиця активності MicroDAO
Створити міграцію `migrations/041_microdao_activity.sql`:
```sql
CREATE TABLE microdao_activity (
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
microdao_slug TEXT NOT NULL REFERENCES city_microdao(slug) ON DELETE CASCADE,
kind TEXT NOT NULL CHECK (kind IN ('post', 'event', 'update')),
title TEXT,
body TEXT NOT NULL,
author_agent_id UUID NULL REFERENCES city_agent(id),
author_name TEXT NULL,
created_at TIMESTAMPTZ NOT NULL DEFAULT now()
);
CREATE INDEX idx_microdao_activity_microdao_created_at
ON microdao_activity (microdao_slug, created_at DESC);
```
#### 3.1.2. Розширити `city_microdao` під метрики (опціонально, але корисно)
Міграція `migrations/042_microdao_stats.sql`:
```sql
ALTER TABLE city_microdao
ADD COLUMN IF NOT EXISTS citizens_count INTEGER NOT NULL DEFAULT 0,
ADD COLUMN IF NOT EXISTS rooms_count INTEGER NOT NULL DEFAULT 0,
ADD COLUMN IF NOT EXISTS agents_count INTEGER NOT NULL DEFAULT 0,
ADD COLUMN IF NOT EXISTS last_update_at TIMESTAMPTZ;
```
> На етапі MVP значення можна просто перераховувати на льоту в репозиторії й не оновлювати ці поля — але стовпці потрібні для майбутнього кешування.
---
### 3.2. Backend: FastAPI (city-service)
#### 3.2.1. Repo-рівень (Python)
Файл: `services/city-service/repo_city.py`
Додати:
```python
async def get_microdao_dashboard(conn, slug: str) -> MicrodaoDashboard:
"""
Aggregates:
- microdao summary
- last 5 activity items
- up to 5 rooms
- up to 6 citizens (agents) linked to this microdao
"""
```
Використати існуючі репо-методи:
* `get_microdao_by_slug`
* `get_microdao_rooms(slug)`
* `get_agents_by_microdao(slug)` або аналог (якщо немає — додати простий SELECT по district/slug mapping).
Нові helper-методи:
```python
async def get_microdao_activity(conn, slug: str, limit: int = 10) -> list[MicrodaoActivity]:
...
async def insert_microdao_activity(conn, activity: CreateMicrodaoActivity) -> MicrodaoActivity:
...
```
#### 3.2.2. Pydantic-моделі
Файл: `services/city-service/models_city.py`
Додати:
```python
class MicrodaoActivity(BaseModel):
id: UUID
microdao_slug: str
kind: str
title: str | None
body: str
author_agent_id: UUID | None
author_name: str | None
created_at: datetime
class CreateMicrodaoActivity(BaseModel):
kind: str = Field(pattern="^(post|event|update)$")
title: str | None = None
body: str
author_agent_id: UUID | None = None
author_name: str | None = None
class MicrodaoDashboard(BaseModel):
microdao: MicrodaoSummary
stats: MicrodaoStats
recent_activity: list[MicrodaoActivity]
rooms: list[RoomSummary]
citizens: list[PublicCitizenSummary]
```
`MicrodaoStats`:
```python
class MicrodaoStats(BaseModel):
rooms_count: int
citizens_count: int
agents_count: int
last_update_at: datetime | None
```
#### 3.2.3. Routes (FastAPI)
Файл: `services/city-service/routes_city.py`
Додати endpoints:
```python
@router.get("/microdao/{slug}/dashboard", response_model=MicrodaoDashboard)
async def get_microdao_dashboard(slug: str, conn=Depends(get_conn)):
return await repo_city.get_microdao_dashboard(conn, slug)
@router.get("/microdao/{slug}/activity", response_model=list[MicrodaoActivity])
async def list_microdao_activity(slug: str, limit: int = 20, conn=Depends(get_conn)):
return await repo_city.get_microdao_activity(conn, slug, limit)
@router.post("/microdao/{slug}/activity", response_model=MicrodaoActivity, status_code=201)
async def create_microdao_activity(
slug: str,
payload: CreateMicrodaoActivity,
conn=Depends(get_conn),
# TODO: optional auth / orchestrator check
):
return await repo_city.create_microdao_activity(conn, slug, payload)
```
Для MVP можна не чіпати авторизацію — припустити, що виклик робить orchestrator через адмін-UI.
---
### 3.3. Frontend: Next.js (apps/web)
#### 3.3.1. API-клієнт
Файл: `apps/web/src/lib/api.ts`
Додати:
```ts
export async function fetchMicrodaoDashboard(slug: string): Promise<MicrodaoDashboard> {
const res = await fetch(`${CITY_API_BASE_URL}/microdao/${slug}/dashboard`, { cache: "no-store" });
if (!res.ok) throw new Error("Failed to load microdao dashboard");
return res.json();
}
export async function fetchMicrodaoActivity(slug: string): Promise<MicrodaoActivity[]> {
const res = await fetch(`${CITY_API_BASE_URL}/microdao/${slug}/activity?limit=20`, { cache: "no-store" });
if (!res.ok) throw new Error("Failed to load microdao activity");
return res.json();
}
```
Типи `MicrodaoDashboard` / `MicrodaoActivity` додати до `apps/web/src/lib/types.ts`.
---
#### 3.3.2. Структура сторінки `/microdao/[slug]`
Файл (вже існує):
`apps/web/src/app/microdao/[slug]/page.tsx`
Перетворити на **Dashboard Layout**:
1. На сервері викликати `fetchMicrodaoDashboard(slug)`.
2. Розбити на секції:
```tsx
<MicrodaoHeaderCard dashboard={dashboard} /> // hero
<MicrodaoDashboardGrid>
<MicrodaoActivitySection activity={dashboard.recent_activity} />
<MicrodaoRoomsSection rooms={dashboard.rooms} slug={slug} />
<MicrodaoTeamSection citizens={dashboard.citizens} />
<MicrodaoProjectsSection microdao={dashboard.microdao} /> // placeholder
<MicrodaoTasksSection microdao={dashboard.microdao} /> // placeholder
</MicrodaoDashboardGrid>
```
Нові компоненти:
* `apps/web/src/components/microdao/MicrodaoHeaderCard.tsx`
* `apps/web/src/components/microdao/MicrodaoActivitySection.tsx`
* `apps/web/src/components/microdao/MicrodaoTeamSection.tsx`
* `apps/web/src/components/microdao/MicrodaoProjectsSection.tsx` (stub)
* `apps/web/src/components/microdao/MicrodaoTasksSection.tsx` (stub)
##### MicrodaoHeaderCard
Показати:
* Лого (через `normalizeAssetUrl`)
* Назву, теги (Platform / Core / Active)
* Метрики:
* `{stats.rooms_count} Кімнат`
* `{stats.citizens_count} Громадян`
* `{stats.agents_count} Агентів`
* кнопки:
* "Поспілкуватися з DAARWIZZ" → `href="/city?room=city-lobby"`
* "Створити кімнату" → прокрутка до секції кімнат або модалка.
##### MicrodaoActivitySection
* Заголовок "Новини DAARION" (або назва DAO).
* Список останніх 510 записів:
* заголовок / перші рядки body,
* дата,
* автор (агент або author_name).
* Якщо немає записів — показати `Empty state: "Поки що немає новин"`.
---
### 3.4. Зв'язок з існуючими розділами
* MicroDAO Rooms: у компоненті `MicrodaoRoomsSection` використати вже існуючу логіку створення/видалення кімнат.
* Citizens: `MicrodaoTeamSection` повинна показувати тільки тих громадян, у яких `home_microdao_slug` або district/room mapping вказує на цей MicroDAO. На етапі MVP можна вибрати фіксований набір (наприклад, 6 ключових агентів DAARION) через простий SQL.
---
## 4. План виконання (для Cursor)
### Крок 1. DB міграції
1. Створити `migrations/041_microdao_activity.sql` і `042_microdao_stats.sql`.
2. Запустити міграції локально й на NODE1 (MVP DB).
### Крок 2. Backend
1. Оновити `models_city.py` — додати моделі `MicrodaoActivity`, `CreateMicrodaoActivity`, `MicrodaoStats`, `MicrodaoDashboard`.
2. Оновити `repo_city.py` — реалізувати aggregation для `get_microdao_dashboard` та CRUD для activity.
3. Оновити `routes_city.py` — додати `/microdao/{slug}/dashboard` та `/microdao/{slug}/activity`.
### Крок 3. Frontend
1. Оновити `apps/web/src/lib/types.ts` та `api.ts` (типи й API-клієнт).
2. Переписати `apps/web/src/app/microdao/[slug]/page.tsx`, щоб вона використовувала `fetchMicrodaoDashboard`.
3. Створити компоненти:
* `MicrodaoHeaderCard.tsx`
* `MicrodaoActivitySection.tsx`
* `MicrodaoTeamSection.tsx`
* (stubs) `MicrodaoProjectsSection.tsx`, `MicrodaoTasksSection.tsx`
### Крок 4. DAARION DAO як демо
1. Заповнити хоча б **510 записів `microdao_activity`** для `slug='daarion'` (через SQL або простий admin-endpoint /seed).
2. Прив'язати ключових агентів DAARION до цього MicroDAO (update в таблиці citizens/agents).
3. Перевірити сторінку `/microdao/daarion`:
* лого + title;
* метрики не ламаються (0 — ок, NaN — ні);
* відображаються кімнати DAARION;
* видно список 6+ громадян DAARION;
* CTA "Поспілкуватися з DAARWIZZ" працює.
---
## 7. Що показувати на презентації
1. **МікроDAO список** — DAARION, Energy Union, GreenFood, Soul наверху (pin).
2. **DAARION DAO сторінка**:
* красивий hero-блок як "панель керування DAO";
* блок "Новини DAARION";
* блок "Кімнати DAARION";
* блок "Команда DAARION".
3. **City Rooms** — показати, що одна з кімнат — головний публічний чат (DAARION City Lobby) з DAARWIZZ.
4. **Citizens / Agents** — як реєстр агентів, які прив'язані до DAARION.
Це вже виглядає як **живий кабінет DAO**, а не просто сайт.
---
Якщо хочеш, наступним кроком можу згенерувати окремий `TASK_PHASE_MICRODAO_DASHBOARD_v1_CURSOR_PROMPT.md` з готовим промтом для Cursor (щоб він сам крок-за-кроком виконав усе з цього таска).