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:
478
docs/tasks/TASK_PHASE_MICRODAO_DASHBOARD_v1.md
Normal file
478
docs/tasks/TASK_PHASE_MICRODAO_DASHBOARD_v1.md
Normal 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).
|
||||
|
||||
* Список останніх 5–10 записів:
|
||||
|
||||
* заголовок / перші рядки 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. Заповнити хоча б **5–10 записів `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 (щоб він сам крок-за-кроком виконав усе з цього таска).
|
||||
|
||||
Reference in New Issue
Block a user