feat(rooms): Fix NaN online stats + Add DAARWIZZ CTA on homepage

- Fixed NaN in online stats by using nullish coalescing (?? 0)
- Added members_online, zone, room_type to /api/v1/city/rooms response
- Added DAARWIZZ chat CTA section on homepage with link to city-lobby
- Created task files for next phases:
  - TASK_PHASE_CITY_ROOMS_FINISH_v2.md
  - TASK_PHASE_AGENT_MANAGEMENT_v1.md
  - TASK_PHASE_CITIZENS_DIRECTORY_v1.md
This commit is contained in:
Apple
2025-12-01 09:19:07 -08:00
parent a563ec86a0
commit 649d07ee29
6 changed files with 598 additions and 3 deletions

View File

@@ -0,0 +1,234 @@
# TASK_PHASE_AGENT_MANAGEMENT_v1
## Title
TASK_PHASE_AGENT_MANAGEMENT_v1 — Agent Create/Delete + Crew Teams Integration
## 1. Overview
Implement full lifecycle management for DAARION agents:
* Create agent
* Delete agent
* Assign to:
* Node
* MicroDAO
* District (city zone)
* Crew (team)
* Prepare room integration for CrewAI teams (each crew has its own room).
This task **does not** change runtime orchestration logic (NATS, Router, etc.) — only registry & UI.
---
## 2. Current State
* Database:
* Table `agents` already exists (used by city-service).
* There is a `sync-node2-dagi-agents.py` script populating 50 agents for NODE2 from `agents_city_mapping.yaml`.
* Backend:
* `repo_city.get_node_agents(node_id)` returns agents for Node Cabinet.
* `repo_city.get_agents(...)` provides listing for `/agents` UI.
* Existing fields:
* `node_id`
* `district`
* `primary_room_slug`
* `model`
* `is_public`
* `home_node_id`
* `home_microdao_id`
* `crew_team_key`
* Frontend:
* `/agents` page lists agents with:
* status (online/offline)
* node badge (НОДА1 / НОДА2)
* visibility badges (Personal / Public)
* `/agents/[slug]` has Identity tab with visibility toggles.
* No button to create or delete an agent from UI.
---
## 3. Goals
1. **Create agent** from UI:
* Minimal form.
* Pre-fill microDAO/context when creation initiated from MicroDAO.
2. **Delete agent** from UI:
* Soft-delete (mark as deleted/archived).
3. **Crew / Team attribute**:
* Each agent may belong to a `crew_team_key`.
4. Prepare for **Crew rooms** integration:
* For each crew (unique `crew_team_key`) we can later auto-create a room.
---
## 4. Database & Backend Tasks
### 4.1. Verify agents table fields
Existing fields to use:
* `node_id` - which node agent belongs to
* `home_node_id` - home node
* `home_microdao_id` - home MicroDAO
* `district` - city district key
* `crew_team_key` - crew/team key
* `is_archived` - soft delete flag
* `deleted_at` - deletion timestamp
### 4.2. Models & Repo
`services/city-service/models_city.py`:
```python
class CreateAgentRequest(BaseModel):
slug: str
display_name: str
kind: str = "assistant"
role: Optional[str]
model: Optional[str]
node_id: Optional[str]
home_microdao_id: Optional[str]
district: Optional[str]
crew_team_key: Optional[str]
is_public: bool = False
avatar_url: Optional[str]
color_hint: Optional[str]
```
`services/city-service/repo_city.py`:
```python
async def create_agent(data: dict) -> dict:
"""Create new agent in database"""
pool = await get_pool()
# INSERT INTO agents ...
async def delete_agent(agent_id: str) -> bool:
"""Soft delete agent (set is_archived=true, deleted_at=now())"""
pool = await get_pool()
# UPDATE agents SET is_archived = true, deleted_at = NOW() WHERE id = $1
```
### 4.3. API routes
`services/city-service/routes_city.py`:
```python
@router.post("/city/agents")
async def create_agent(body: CreateAgentRequest):
"""Create new agent"""
# Validate slug uniqueness
# Insert into database
# Return created agent
@router.delete("/city/agents/{agent_id}")
async def delete_agent(agent_id: str):
"""Soft delete agent"""
# Set is_archived = true, deleted_at = now()
return {"ok": True, "message": "Agent archived"}
```
---
## 5. Frontend Tasks
Files:
* `apps/web/src/app/agents/page.tsx`
* `apps/web/src/app/agents/new/page.tsx` (new)
* `apps/web/src/app/microdao/[slug]/page.tsx`
* `apps/web/src/app/agents/[agentId]/page.tsx`
### 5.1. API client
```ts
// lib/api/agents.ts
export async function createAgent(payload: CreateAgentPayload) {
return apiClient.post('/city/agents', payload);
}
export async function deleteAgent(id: string) {
return apiClient.delete(`/city/agents/${id}`);
}
```
### 5.2. "New Agent" page
1. On `/agents` page:
* Add button `+ Новий агент` in header.
* Click → navigate to `/agents/new`.
2. New page `/agents/new`:
Form fields:
* Ім'я (`display_name`, required)
* Slug (auto-generated from name, editable)
* Роль / Title (optional)
* Тип агента (`kind`: orchestrator, assistant, specialist, etc.)
* Нода (select: NODE1, NODE2 — fetch from `/nodes`)
* MicroDAO (dropdown, fetch from `/microdao`)
* Район (dropdown: leadership, security, engineering, etc.)
* Команда / Crew (free text or dropdown)
* Модель (optional: list of Swapper models)
* Видимість:
* `is_public` (switch, default false)
* Аватар URL (optional)
* Колір (optional color picker)
On submit:
* POST `/city/agents`
* Redirect to `/agents/[slug]`.
### 5.3. "Create Agent" from MicroDAO
On `/microdao/[slug]`:
* In "Агенти MicroDAO" section, add button:
```tsx
<Link href={`/agents/new?microdao=${microdaoId}`}>
<Button>
<Plus className="w-4 h-4 mr-2" />
Створити агента
</Button>
</Link>
```
* On `/agents/new`:
* If query `?microdao=...` present — pre-fill MicroDAO field.
### 5.4. Delete Agent action
On `/agents/[agentId]`:
* Add "Видалити агента" button in settings/danger section.
Flow:
* Confirm dialog: "Видалити агента? Його не буде видно у місті."
* On confirm → `DELETE /city/agents/{id}`.
* After success: redirect to `/agents` with toast.
---
## 6. Crew Teams (Preparation)
This task prepares data for Crew rooms:
* Each agent can have `crew_team_key`.
* Display `crew_team_key` as badge on agent card.
* Later: auto-create room for each unique `crew_team_key`.
---
## 7. Acceptance Criteria
1. `/agents` page has `+ Новий агент` button.
2. `/agents/new` allows creating a new agent.
3. `/microdao/[slug]` has "Створити агента" button.
4. `/agents/[agentId]` has "Видалити агента" button.
5. Deleted agents disappear from listings.
6. `crew_team_key` is visible on agent cards.

View File

@@ -0,0 +1,88 @@
# TASK_PHASE_CITIZENS_DIRECTORY_v1
## Title
TASK_PHASE_CITIZENS_DIRECTORY_v1 - Public Citizens Directory and Agent Visibility
## 1. Overview
Make /citizens page fully functional as public AI-citizens directory:
* Show list of agents marked as public.
* Support filters: search, district, agent type.
* Connect Publichnyi hromadianyn mista toggle in agent Identity tab to this directory.
## 2. Current State
* /citizens page exists, but shows:
* Found citizens: 0
* Error: Failed to fetch citizens
* Agents page /agents shows many agents (including NODE2 DAGI agents).
* On agent Identity tab there is toggle:
* Publichnyi hromadianyn mista
* Rezhym vydymosti: Publichnyi / Tilky MicroDAO
* Backend likely missing:
* Proper get_public_citizens() query.
* Or route /public/citizens is broken.
## 3. Goals
1. Make /citizens page show a list of public agents.
2. Wire visibility controls on agent Identity tab to is_public field.
3. Filters work:
* Search by name/title/tagline.
* District filter.
* Agent type filter.
## 4. Backend Tasks
### 4.1. Citizen model
Add CitizenSummary model with fields:
- id, slug, display_name, role, public_tagline
- district, public_district, home_microdao_id
- kind, node_id, avatar_url, color_hint, status
### 4.2. Repo: get_public_citizens
Query agents WHERE is_public = true AND deleted_at IS NULL AND is_archived = false.
Apply filters for district, kind, and search.
### 4.3. API route
GET /public/citizens with optional query params: search, district, kind.
### 4.4. Update agent visibility
PATCH /city/agents/{agent_id}/visibility to update is_public and visibility_scope.
## 5. Frontend Tasks
### 5.1. Fix Citizens API call
### 5.2. Citizens Page UI with filters
### 5.3. Citizen Card Component
### 5.4. Wire Identity Tab to visibility API
## 6. Acceptance Criteria
1. /citizens page loads without error.
2. At least one agent with is_public=true appears in Citizens Directory.
3. Filters work.
4. Changing visibility on /agents/[slug] Identity tab updates /citizens.
5. No leakage of deleted/archived agents.
## 7. Districts Reference
* leadership - Leadership Hall
* system - System Control Center
* engineering - Engineering Lab
* marketing - Marketing Hub
* finance - Finance Office
* web3 - Web3 District
* security - Security Bunker
* vision - Vision Studio
* rnd - R&D Laboratory
* memory - Memory Vault

View File

@@ -0,0 +1,241 @@
# TASK_PHASE_CITY_ROOMS_FINISH_v2
## Title
TASK_PHASE_CITY_ROOMS_FINISH_v2 — MicroDAO Rooms + City Lobby CTA + Online Stats Fix
## 1. Overview
Finish the DAARION City chat layer:
1. City Rooms page:
* Map + List already work.
* Fix `NaN` for online users count.
2. MicroDAO Rooms:
* Each MicroDAO must have its own rooms section.
* Ability to create new rooms from MicroDAO cabinet.
3. City Lobby CTA:
* Main public chat with DAARWIZZ should be accessible directly from the homepage and top navigation.
All changes must use existing architecture: `city-service` (FastAPI + Postgres) and `apps/web` (Next.js / React).
---
## 2. Current State (as of 2025-12-01)
* Backend:
* `scripts/seed_city_rooms.py` creates ~31 rooms (district rooms, lobbies, city lobby, etc.).
* `city-service` exposes `/api/v1/city/rooms` (already used by UI).
* Rooms table already contains:
* `id`
* `slug`
* `name` (title)
* `description`
* `zone` (district key)
* `room_type` (e.g. `city`, `district`, `microdao`, etc.)
* `is_public` (bool)
* `microdao_id` (nullable, for MicroDAO rooms)
* Frontend:
* `/city` (City Rooms):
* Map tab: grid of districts, shows total rooms and online counters.
* List tab: renders all rooms with description.
* At the bottom: summary cards: `Kімнат: 31`, `Онлайн: NaN`.
* MicroDAO pages (`/microdao/[slug]`):
* Currently have no dedicated "MicroDAO Rooms" section.
* Homepage (`/`):
* No direct CTA to "DAARION City Lobby".
---
## 3. Goals
1. **Fix online counter**:
* Never show `NaN` on the City Rooms page.
2. **MicroDAO Rooms UI**:
* On `/microdao/[slug]` display:
* List of rooms belonging to this MicroDAO.
* Button "Створити кімнату".
3. **City Lobby CTA**:
* On homepage and/or main menu:
* Button "Поспілкуватися з DAARWIZZ" that opens `DAARION City Lobby`.
---
## 4. Backend Tasks
### 4.1. Room model & repository
Files:
* `services/city-service/models_city.py`
* `services/city-service/repo_city.py`
* `services/city-service/routes_city.py`
1. **Ensure Room model exposes needed fields:**
```python
class CityRoomSummary(BaseModel):
id: str
slug: str
name: str
description: Optional[str]
zone: Optional[str]
room_type: str
microdao_id: Optional[str]
is_public: bool
members_online: int = 0 # ← must always be integer (0 by default)
```
2. **Repo method for city rooms:**
* `get_all_rooms()` must:
* Return `members_online` as integer (use `COALESCE(..., 0)`).
3. **Repo method for MicroDAO rooms:**
Add/verify method:
```python
async def get_microdao_rooms(microdao_id: str) -> List[dict]:
"""Get rooms for specific MicroDAO"""
# WHERE microdao_id = $1 AND is_public = true OR owner_type = 'microdao'
```
4. **API routes:**
* Verify endpoint: `GET /api/v1/city/rooms` returns `members_online: int`.
* Verify endpoint: `GET /city/microdao/{slug}/rooms` returns MicroDAO rooms.
* Add endpoint for room creation:
```python
class CreateRoomRequest(BaseModel):
title: str
description: Optional[str]
room_type: str = "microdao"
is_public: bool = False
@router.post("/city/microdao/{slug}/rooms")
async def create_microdao_room(slug: str, body: CreateRoomRequest):
# Create room for MicroDAO
```
5. **Online stats summary:**
* Add/verify endpoint: `GET /api/v1/city/rooms/summary`
* Returns: `{"rooms_total": int, "online_total": int}`
---
## 5. Frontend Tasks
Files:
* `apps/web/src/app/city/page.tsx`
* `apps/web/src/app/microdao/[slug]/page.tsx`
* `apps/web/src/app/page.tsx` (homepage)
* `apps/web/src/components/microdao/MicrodaoRoomsSection.tsx` (new)
### 5.1. Fix `NaN` online stats
1. In `city/page.tsx`:
* Ensure online count uses fallback:
```tsx
const onlineTotal = summary?.online_total ?? 0;
// or
const onlineTotal = Number(summary?.online_total) || 0;
```
2. If API returns `null` or `undefined`, display `0`.
### 5.2. MicroDAO Rooms section
In `apps/web/src/app/microdao/[slug]/page.tsx`:
1. Add API call:
```ts
const rooms = await fetchMicrodaoRooms(slug); // GET /city/microdao/{slug}/rooms
```
2. Render new section:
```tsx
<section className="space-y-4">
<div className="flex items-center justify-between">
<h2 className="text-xl font-semibold">Кімнати MicroDAO</h2>
<Button onClick={openCreateRoomModal}>
<Plus className="w-4 h-4 mr-2" />
Створити кімнату
</Button>
</div>
{rooms.length === 0 ? (
<p className="text-muted-foreground">
Ще немає кімнат. Створіть першу кімнату для цієї платформи.
</p>
) : (
<div className="grid gap-3">
{rooms.map((room) => (
<MicrodaoRoomCard key={room.slug} room={room} />
))}
</div>
)}
</section>
```
3. `MicrodaoRoomCard` component:
* title
* description
* room_type (badge)
* visibility (Public/Private badge)
* members_online
4. "Create Room" modal:
* Назва кімнати (title, required)
* Опис (optional)
* Тип (select: Lobby, Governance, Project, Crew)
* Видимість (switch: Public / Private, default = Private)
* On submit → POST `/city/microdao/{slug}/rooms`
### 5.3. City Lobby CTA
1. On homepage (`apps/web/src/app/page.tsx`):
```tsx
<section className="py-12 text-center">
<h2 className="text-2xl font-bold mb-4">
Головний публічний чат DAARION City
</h2>
<p className="text-muted-foreground mb-6">
Поспілкуватися з DAARWIZZ та іншими громадянами міста.
</p>
<Link href="/city?room=city-lobby">
<Button size="lg" className="gap-2">
<MessageCircle className="w-5 h-5" />
Поспілкуватися з DAARWIZZ
</Button>
</Link>
</section>
```
2. Optionally add to navigation header:
* New item: "City Lobby" → `/city?room=city-lobby`
---
## 6. Acceptance Criteria
1. City Rooms page shows:
* Correct number of rooms.
* Online count is integer (0 allowed), never `NaN`.
2. MicroDAO page (`/microdao/daarion`, etc.) shows:
* "Кімнати MicroDAO" section.
* Ability to create a room.
* Newly created room appears in list.
3. Homepage shows:
* CTA "Поспілкуватися з DAARWIZZ".
* Clicking the button opens City Lobby room.
4. All routes work both on NODE1 and NODE2.