diff --git a/docs/superpowers/specs/2026-06-11-agent-oversight-timeline-design.md b/docs/superpowers/specs/2026-06-11-agent-oversight-timeline-design.md new file mode 100644 index 0000000..b274ca4 --- /dev/null +++ b/docs/superpowers/specs/2026-06-11-agent-oversight-timeline-design.md @@ -0,0 +1,107 @@ +# 에이전트 횡단 오버사이트 타임라인 — 설계 + +작성일: 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) + └─ + └─ 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 + 테스트 + 빌드 검증