Files
web-page/docs/superpowers/specs/2026-06-11-agent-oversight-timeline-design.md
2026-06-11 08:44:44 +09:00

108 lines
5.6 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 에이전트 횡단 오버사이트 타임라인 — 설계
작성일: 2026-06-11
대상 repo: `web-ui` (프론트엔드)
연관 백엔드: ✅ 완료 (`GET /api/agent-office/activity` 필터 지원, main `2c2828c`)
## 배경 / 목적
3개 자율 에이전트(stock 보유종목·insta 발급·lotto 진화)가 모두 도는 상태에서
"팀이 무엇을·언제·왜 했나"를 **한 화면에서** 보는 에이전트 횡단 오버사이트(CEO 가시화) 기능.
현재 web-ui에는 `/lotto/evolver` 탭의 lotto 전용 `LottoActivityTimeline`만 존재.
통합 `/activity`(전 에이전트 대상)를 소비하는 횡단 뷰가 없다.
## 백엔드 응답 shape (라이브 검증 완료)
```
GET /api/agent-office/activity?agent_id=&type=task|log&status=&days=&limit=&offset=
→ { items: [...], total: N }
```
- **task item**: `{ type:'task', agent_id, task_id, message, created_at, task_type, status, completed_at, duration_seconds }`
- **log item**: `{ type:'log', agent_id, task_id, message, created_at, level }`
- `status`는 task 전용(`type=log`에 주면 무시). injection 안전(? 바인딩 + 브랜치 선택).
검증 메모:
- 무필터 `total`이 65,599건 → **기본 `days=7` 필터 필수**(task 기준 110건으로 감소).
- `requires_approval` 필드는 **존재하지 않음**`status:'pending'`을 진행/대기 강조로 처리.
- `agent_id` 값이 `AGENT_META` 키(stock/music/insta/realestate/lotto)와 일치 → 색상/이미지 재사용.
## 아키텍처
AgentOffice는 단일 화면(TopBar + 3×3 AgentGrid + 우측 패널) 구조.
우측 패널은 `selectedAgent` 상태로 분기:
- `null` → (기존) `EmptyDetailPanel variant="initial"`**`ActivityTimeline`으로 교체**
- `placeholder-N``EmptyDetailPanel variant="placeholder"` (유지)
- active agent id → `SidePanel` (유지)
즉 **에이전트 미선택 시 기본 우측 패널이 횡단 타임라인**이 되고, 그리드와 항상 동시 노출.
항목/그리드 클릭으로 해당 에이전트 SidePanel로 전환.
## 신규/변경 파일
| 파일 | 역할 |
|------|------|
| `src/api.js` | `agentActivity({agent_id,type,status,days,limit,offset})` 추가 — 빈 값 제외 쿼리스트링 빌드 + GET `/api/agent-office/activity` |
| `src/pages/agent-office/AgentOffice.jsx` | `selectedAgent===null` 분기를 `EmptyDetailPanel``ActivityTimeline`(props: `refreshTrigger`, `onSelectAgent`)로 교체 |
| `src/pages/agent-office/hooks/useActivityFeed.js` | items/offset/total/hasMore/loading/error/filters 상태 관리 |
| `src/pages/agent-office/components/ActivityTimeline.jsx` | 컨테이너: 헤더 + `ActivityFilters` + 리스트 + 무한스크롤 sentinel + 상태(loading/empty/error/end) |
| `src/pages/agent-office/components/ActivityFilters.jsx` | 필터 4종(agent 색칩 / type / status / days). `type==='log'`일 때 status 비활성 |
| `src/pages/agent-office/components/ActivityItem.jsx` | 한 행: agent 색·이미지 + message + 상태/level 뱃지 + 상대시간 + duration. 클릭 → `onSelectAgent(agent_id)` |
| `src/pages/agent-office/AgentOffice.css` | 타임라인/필터/항목 스타일 (designer 스킬로 마감) |
## 데이터 흐름
```
AgentOffice (selectedAgent===null)
└─ <ActivityTimeline refreshTrigger={refreshTrigger} onSelectAgent={handleSelectAgent} />
└─ useActivityFeed(filters)
• mount / 필터 변경 → offset=0 fetch → items 교체
• loadMore (sentinel 교차) → offset += limit → items append
• refreshTrigger 변경 → offset=0 재조회 → items 교체 (WS 실시간 연동)
└─ ActivityItem onClick → onSelectAgent(agent_id) → SidePanel로 전환
```
`handleSelectAgent`는 기존 콜백 재사용(선택 + `clearNotifications`).
## 필터 기본값
`days=7`, `type=all`, `status=all`, `agent=all`, `limit=30`(페이지당).
## 상태 / 비주얼 매핑
- task `status`: `succeeded` → 초록 ✓ / `failed` → 빨강 ✗ / `pending`·`working` → 앰버 펄스 ⏳(강조)
- log `level`: `error` → ❌ / `warning` → ⚠️ / `info` → ·
- agent 색상: `AGENT_META[agent_id].color`, 미지정 agent → 회색 `#6b7280`
- `offset >= total` → "더 이상 활동 없음" / 무한스크롤은 IntersectionObserver
## 상태 처리(엣지)
- 첫 페이지 로딩 → 스피너/스켈레톤
- 빈 결과 → "최근 N일 활동 없음"
- fetch 실패 → 인라인 에러 + 재시도 버튼
- 리스트 끝 → end-of-list 표시, sentinel 관찰 중단
## 테스트 (TDD, vitest + RTL — 기존 패턴 따름)
- `useActivityFeed`: 필터 변경 시 offset 리셋 + items 교체 / loadMore append / refreshTrigger 재조회 / `hasMore = items.length < total` 계산 (api mock)
- `ActivityItem`: task vs log 렌더 분기, status/level 뱃지 클래스, 클릭 시 `onSelectAgent(agent_id)` 호출
- `ActivityFilters`: `type==='log'`일 때 status select 비활성, 필터 변경 시 onChange 호출
## 비범위 (YAGNI)
- 별도 라우트(`/agent-office/activity`) 미생성 — 기본 우측 패널 통합으로 충분
- 기존 `getActivityFeed(limit, offset)` 헬퍼는 lotto evolver 등에서 사용 여부 확인 후 유지(신규 `agentActivity`와 공존, 무리한 통합 안 함)
- `LottoActivityTimeline`(`kind/ts/payload` shape)은 다른 엔드포인트 소비 → 건드리지 않음
- CSV/export, 검색어 필터 등 부가기능 제외
## 구현 순서
1. `agentActivity` api 헬퍼 추가
2. `useActivityFeed` 훅 (TDD)
3. `ActivityItem` / `ActivityFilters` (TDD)
4. `ActivityTimeline` 컨테이너 조립
5. `AgentOffice.jsx` 분기 교체
6. designer 스킬로 CSS 마감
7. lint + 테스트 + 빌드 검증