# 아리(Ari) — AI Life OS · 개발 개요

> 일·삶을 한곳에서 관리하는 한국어 AI 개인비서 **아리**의 MVP 개발 진입 문서.
> 핵심 철학: **"적을 때는 분류하지 않는다 — 분류·배치·자동화는 아리가."** 사용자는 *읽고 탭 한 번*, 나머지는 아리가 합니다.

**이 문서는 `dev/` 문서 세트의 일부입니다 — 가장 먼저 이 `overview.md`를 읽으세요.** 이후 phase 문서(`phase-0-foundation.md` → `phase-1-design-system.md` → `phase-2-backend.md` → `phase-3-tasks.md` → `phase-4-inbox.md` → `phase-5-dashboard.md` → `phase-6-integration.md`)를 빌드 순서대로 진행합니다. 각 phase 문서로의 정확한 연결은 [§11 개발 Phase 로드맵](#11-개발-phase-로드맵)과 [§14 이 문서 세트 사용법](#14-이-문서-세트-사용법)에 있습니다.

> **MVP 이후(포스트-MVP)**: 나머지 10개 페이지와 연합·실연동·능동 에이전트·프로덕션은 별도 세트로 문서화되어 있습니다 — `post-mvp-overview.md`(포스트-MVP 진입 문서)부터 시작해 `phase-7-approvals-automation.md` → `phase-8-calendar-meetings.md` → `phase-9-mail-notifications.md` → `phase-10-research-travel.md` → `phase-11-life-care.md` → `phase-12-daily-narrative.md` → `phase-13-integrations.md` → `phase-14-proactive-agent.md` → `phase-15-production.md` 순으로 진행합니다. 이 세트는 위 MVP 규약(스택·토큰·데이터모델·API·테스트)을 그대로 상속합니다.

원본 디자인(픽셀 충실 재현 기준)은 다음 경로에 보관되어 있습니다. 이 문서의 모든 값(색 HEX, px, 클래스명, 한국어 문구, 데이터 필드)은 이 파일들에서 그대로 인용했습니다.

```
REF = /Users/kim1634/mycloud/personal/workspace/ari_assistant/workspace/design-reference
  REF/대시보드.html · REF/인박스.html · REF/작업.html      (MVP 3개 페이지 진입 HTML)
  REF/assets/dash.css                                     (디자인 토큰의 기준 :root)
  REF/assets/shell.jsx                                    (Topbar/SubRail/Icon, MAIN 13항목, P 아이콘 맵)
  REF/assets/data.js                                      (대시보드 시드: user/schedule/goals/...)
  REF/assets/tasks-data.js                                (작업 트리·people·columns·scaffold)
  REF/assets/tasks-risk.jsx                               (리스크 레이더 계산 규칙, TODAY=8)
  REF/assets/sinbox-data.js                               (인박스 캡처·분류 시드)
  REF/assets/approve-data.js                              (결재함 읽기전용 시드)
  REF/PROJECT-README.md · REF/HANDOFF-README.md           (제품 설명·핸드오프)
```

---

## 0. 목차

1. [개요 & 목표](#1-개요--목표)
2. [선행 조건 / 산출물](#2-선행-조건--산출물)
3. [제품 비전 & 철학](#3-제품-비전--철학)
4. [데모 페르소나(지우)와 한 주의 시드 데이터](#4-데모-페르소나지우와-한-주의-시드-데이터)
5. [MVP 범위 & 13페이지 비전 경계](#5-mvp-범위--13페이지-비전-경계)
6. [시스템 아키텍처](#6-시스템-아키텍처)
7. [기술 스택 결정과 근거](#7-기술-스택-결정과-근거)
8. [리포지토리 구조](#8-리포지토리-구조)
9. [데이터 모델 개요](#9-데이터-모델-개요)
10. [REST API 개요](#10-rest-api-개요)
11. [디자인 시스템 요약](#11-디자인-시스템-요약)
12. [페이지 연합(federation) 흐름](#12-페이지-연합federation-흐름)
13. [개발 Phase 로드맵](#13-개발-phase-로드맵)
14. [LLM(Ollama) 추상화 & 분류 계약](#14-llmollama-추상화--분류-계약)
15. [상태 처리 & 엣지 케이스 (전역 원칙)](#15-상태-처리--엣지-케이스-전역-원칙)
16. [테스팅 & 검증](#16-테스팅--검증)
17. [완료 기준 (Definition of Done)](#17-완료-기준-definition-of-done)
18. [용어집](#18-용어집)
19. [이 문서 세트 사용법](#19-이-문서-세트-사용법)

> (위 §번호는 본 진입 문서 내부의 절 번호입니다. CONTRACT 아웃라인 항목과 1:1 대응하며 일부 절은 통합되어 있습니다.)

---

## 1. 개요 & 목표

이 문서는 **아리 MVP**의 진입 문서이자 단일 출처(single source of truth)입니다. 제품 비전, 데모 데이터, 아키텍처, 스택, 데이터 모델, REST API, 디자인 시스템, 페이지 연합 흐름, 개발 로드맵을 총괄합니다. 다른 phase 문서는 이 문서의 정의를 **그대로 참조**하며, 임의로 변경하지 않습니다.

**이 phase(문서)가 끝나면 무엇이 동작하는가**: 직접적으로 코드가 동작하지는 않습니다 — 이 문서는 *지도*입니다. 그러나 이 문서를 읽고 나면 개발자는 (1) 무엇을 만드는지, (2) 어떤 기술로, (3) 어떤 순서로, (4) 어떤 데이터/계약으로 만들어야 하는지를 완전히 이해하고 `phase-0-foundation.md`로 바로 진입할 수 있습니다.

**MVP의 한 줄 정의**: 한국어 사용자 *지우*가, 인박스에 자유롭게 적은 한 줄을 아리가 작업/일정/아이디어로 자동 분류하고, 작업 페이지에서 칸반으로 관리하며 리스크 레이더가 마감·쏠림·의존성을 경고하고, 대시보드에서 하루를 한눈에 집계해 보는 — **로컬 완결형 웹앱(빌드/배포 가능)**.

---

## 2. 선행 조건 / 산출물

### 선행 조건 (의존 phase)
- 없음. 이 문서는 phase 0의 선행 문서이자 전 phase의 인덱스입니다.

### 산출물 (Deliverables)
| 산출물 | 내용 |
|---|---|
| 제품 정의 | 비전·철학·페르소나·MVP 경계 (§3~§5) |
| 아키텍처 그림 | 브라우저↔FastAPI↔SQLite↔Ollama 데이터 흐름 (§6) |
| 스택 결정표 | 선택 기술과 근거 (§7) |
| 리포 트리 | `frontend/` + `backend/` 모노레포 전체 구조 (§8) |
| 데이터 모델 표 | 엔티티·필드·관계 (§9) |
| API 계약 표 | 메서드/경로/용도/요청·응답 예시 (§10) |
| 디자인 토큰 요약 | 색/폰트/글래스/아이콘/라이트·다크 (§11) |
| 연합 흐름도 | 캡처→분류→작업→리스크→대시보드 (§12) |
| 로드맵 표 | phase 0~6 목표·산출물·의존성·작업량 (§13) |
| LLM 계약 요약 | Provider 추상화 + 분류 스키마 + 골든 케이스 (§14) |
| 용어집 | 인박스/결재함/리스크 레이더/sphere/scaffold 등 (§18) |

---

## 3. 제품 비전 & 철학

아리는 *할 일 앱*이 아니라 **"Life OS"** 를 지향합니다. 핵심 가치는 세 개의 문장으로 압축됩니다.

### 3.1 "적을 때는 분류하지 않는다 — 분류·배치·자동화는 아리가."
사용자의 인지 부담을 0에 가깝게 만드는 것이 1순위입니다. 사용자는 떠오른 것을 **인박스에 한 줄 던지기만** 하면 됩니다. 어느 프로젝트인지, 작업인지 일정인지, 언제 할지는 **아리가** 정합니다. (원본 `sinbox-data.js` 주석: *"적을 때는 분류하지 않는다 — 분류·배치·자동화는 아리가."*)

예) `"다음 주에 한국 놀러가는 비행기 티켓 사기"` 한 줄 → 아리가 **작업 / 개인 영역 / `개인 › 여행 — 한국` 프로젝트 / 가격 추적 알림**까지 자동 처리. 사용자는 결과를 *읽고 탭 한 번*으로 확정.

### 3.2 "할까요?"가 아니라 "이미 해뒀어요"
아리는 묻고 기다리지 않습니다. **미리 처리한 뒤 승인/되돌리기**를 제공합니다(원본 `approve-data.js` 주석: *"아리는 '할까요?'라고 묻지 않고 미리 해둔다."*).
- **위험도 낮음(low)**: 이미 처리. *되돌리기만* 제공. 예) `"치과 예약을 16:00로 옮겼어요"`(`undoLabel: "원래 시간으로"`).
- **위험도 높음(high)**: 보내기/결제/타인 전달 등은 *사용자 확인 후 실행*. 예) `"현우님께 회신 초안이 준비됐어요"`(`cta: "보내기", alt: "수정"`).

이 원칙은 인박스에서 **분류를 먼저 실행하고 결과를 보여준 뒤 확인받는** 방식(`POST /api/inbox/capture`가 동기 분류까지 수행)으로 MVP에 구현됩니다.

### 3.3 "개인 일은 숨기지 않는다 — 개인 = 프로젝트"
가장 중요한 모델 원칙입니다. **"개인"은 별도 비밀 섹션이 아니라 그냥 하나의 폴더(영역)** 이며, 업무/개인은 **같은 트리에서 '필터'로만** 구분됩니다(원본 `tasks-data.js` 트리: 최상위 `work`(업무)·`life`(개인) 두 폴더가 동급). 아리는 **배치(언제 하면 좋을지)만 추천**하지 강제로 숨기지 않습니다.

원본 분류 `reason` 문구가 이 철학을 그대로 말합니다(`sinbox-data.js`):
> *"작업 트리의 '개인' 아래에 '여행 — 한국' 프로젝트를 만들어 넣었어요 — 따로 섹션이 생기는 게 아니라 다른 작업과 똑같이 보여요."*

배치 추천 예: `엄마 생신 선물 알아보기` → `"주말 오전 블록"`, `엄마 생신 선물` → `"6/20 전"`. 강제 숨김이 아니라 *추천*임에 유의.

---

## 4. 데모 페르소나(지우)와 한 주의 시드 데이터

### 4.1 사용자: 지우(PM)
| 필드 | 값 | 출처 |
|---|---|---|
| 이름 | 지우 | `data.js: user.name` / `tasks-data.js: people.jiwoo.name` |
| 이니셜 | 지 | `data.js: user.initial` (상단바 아바타 `.t-ava`에 표시) |
| 역할 | PM | PROJECT-README |
| 식별 | `is_me = true`, 색 `--blue` | `tasks-data.js: people.jiwoo = { ..., c: "var(--blue)", me: true }` |

### 4.2 팀원 5인 (`person`)
`tasks-data.js: people` 그대로 이식. `color` 값까지 고정.

| id | name | initial | color | is_me |
|---|---|---|---|---|
| `jiwoo` | 지우 | 지 | `var(--blue)` (#4f72e0) | **true** |
| `hyunwoo` | 현우 | 현 | `var(--violet)` (#8b6fd4) | false |
| `minseo` | 민서 | 민 | `var(--green)` (#4e9b66) | false |
| `jaeho` | 재호 | 재 | `var(--coral)` (#df7256) | false |
| `sua` | 수아 | 수 | `oklch(0.66 0.13 200)` | false |

> 주의: 수아의 색은 토큰이 아닌 **리터럴 `oklch(0.66 0.13 200)`** 입니다. 시드 시 `person.color`에 문자열 그대로 저장.

### 4.3 배경: 6월 7~12일 가상의 한 주, 기준일 6월 8일
- 대시보드 표시 날짜: **`today = "6월 7일 일요일"`**(`data.js`). 단, **리스크 계산 기준 `TODAY = 8`**(6월 8일, `tasks-risk.jsx` 7번째 줄 `const TODAY = 8;`). 이 둘은 의도적으로 다릅니다 — 대시보드 날짜 라벨과 리스크 마감 비교 기준일을 혼동하지 마세요. 백엔드 `services/risk.py`는 `TODAY=8`을 기준 상수로 이식합니다.
- 작업 마감(`due`)은 `MM-DD` 문자열(예 `"06-08"`, `"06-14"`, `"06-20"`). 리스크 계산은 `due.split("-")[1]`(일)만 정수로 비교(`dueD`). 표시는 `6/{일}`(`dueTxt`).

### 4.4 시드 데이터 한눈에 (출처별)
| 영역 | 시드 출처 | 핵심 시드 항목(예) |
|---|---|---|
| 작업 트리 | `tasks-data.js: tree` | 업무(`work`)▸경영 전략/온보딩 리디자인/팀 운영, 개인(`life`)▸일상/여행 — 한국/가족 |
| 작업 | `tasks-data.js: tasks` | `k1` 분기 리포트 초안 마무리(doing, due 06-08, 높음), `k20` 한국행 비행기 티켓 구매(life-trip), `k14` 데이터 전처리 파이프라인(waiting, delegated) 등 |
| 인박스 | `sinbox-data.js: items` | `s1`~`s4` (골든 분류 케이스가 곧 시드) |
| 대시보드 일정 | `data.js: schedule` | 09:30 스탠드업, 11:00 디자인 리뷰, 14:00 분기 전략 미팅(`soon:true`), 16:30 1:1—민서님 |
| 목표 | `data.js: goals` | 분기 OKR—리텐션 68%, 주 4회 운동 75%, '딥 워크' 책 완독 40% |
| 결재함(읽기전용) | `approve-data.js: items` | a1~a6, `savedToday: "47분"`, `autoCountNight: 7` |
| 아침 브리핑 | `data.js: weather/commute/sleep/briefingNote` | 24°·맑음, 출근 23분, 수면 7시간 12분, 브리핑 노트(HTML 포함) |

> 날씨 값 주석: `weather_temp = 24`(현재 기온), `weather_cond`의 "한낮 28°" = 일 최고 기온 — **의도적으로 다른 값**입니다(원본 `data.js` 근거). 두 값을 혼동하지 마세요.

> `briefingNote`, 작업 `notes`는 **HTML 문자열**을 포함합니다(`<b>`, `<h3>`, `<blockquote>`). 렌더 시 신뢰된 시드로 취급하되 [§15](#15-상태-처리--엣지-케이스-전역-원칙) 새니타이즈 원칙을 참조.

---

## 5. MVP 범위 & 13페이지 비전 경계

### 5.1 전체 비전 — 13페이지
원본은 13개 화면(상단 내비 `MAIN` 배열, `shell.jsx`)을 가집니다.

`대시보드 · 인박스 · 결재함(badge 3) · 자동화 · 여정 · 일정 · 작업(badge 4) · 메일 · 알림(badge 6) · 리서치 · 여행 · 라이프 · 하루 마감`

### 5.2 MVP — 페이지 3개만 구현
| # | 라우트 | 라벨 | MVP | 담당 phase 문서 |
|---|---|---|---|---|
| 1 | `/tasks` | 작업 | ✅ 구현 | `phase-3-tasks.md` |
| 2 | `/inbox` | 인박스 | ✅ 구현 | `phase-4-inbox.md` |
| 3 | `/dashboard` | 대시보드 | ✅ 구현 | `phase-5-dashboard.md` |
| 4~13 | 결재함·자동화·여정·일정·메일·알림·리서치·여행·라이프·하루 마감 | — | ❌ **"준비 중" 플레이스홀더** | `phase-1-design-system.md` (placeholder 라우팅) |

### 5.3 경계 규칙 (반드시 지킬 것)
1. **상단 내비는 13항목 전부 노출**합니다(내비 일관성). `shell.jsx`의 `MAIN` 배열(13항목·badge·icon·라벨)을 그대로 `frontend/components/Topbar.tsx`로 이식합니다.
2. MVP 외 10개 항목은 클릭 시 **`/(placeholder)` 라우트의 "준비 중" 화면**으로 이동(앱 셸·테마는 동일 유지).
3. **대시보드의 결재함/일정 요약 카드는 시드 데이터 기반 읽기 전용**으로 표시합니다(결재함 페이지 자체는 MVP 외이지만, 요약 카드는 `approve-data.js`/`data.js` 시드로 채움 → `GET /api/dashboard`가 `approvals_summary`/`schedule` 제공).
4. 배지 숫자도 고정 노출: 결재함 `3`, 작업 `4`, 알림 `6`(원본 `MAIN`의 `badge` 값). 단 작업/결재함 배지는 향후 동적화 가능하도록 `GET /api/dashboard`의 `badges:{appr,task,noti}`로 공급.

---

## 6. 시스템 아키텍처

### 6.1 텍스트 아키텍처 다이어그램
```
┌──────────────────────────────────────────────────────────────────────────┐
│  브라우저                                                                    │
│  ┌────────────────────────────────────────────────────────────────────┐   │
│  │  Next.js (App Router, React, TypeScript)                            │   │
│  │  app/layout.tsx  =  ThemeProvider(next-themes) + <Topbar/>          │   │
│  │  ┌──────────────┐ ┌──────────────┐ ┌──────────────┐                │   │
│  │  │ /tasks       │ │ /inbox       │ │ /dashboard   │  /(placeholder) │   │
│  │  │ 칸반/리스트   │ │ 캡처→분류     │ │ 브리핑·요약    │  "준비 중"      │   │
│  │  │ ·리스크       │ │ ·확인·실체화  │ │ ·집계         │  (MVP 외 10)   │   │
│  │  └──────────────┘ └──────────────┘ └──────────────┘                │   │
│  │         │  lib/api.ts (fetch wrapper) · lib/types.ts (1:1 schemas)  │   │
│  └─────────┼──────────────────────────────────────────────────────────┘   │
└────────────┼───────────────────────────────────────────────────────────────┘
             │  HTTP/JSON  (prefix /api)   ·   dev: Next rewrites → :8000
             ▼
┌──────────────────────────────────────────────────────────────────────────┐
│  FastAPI (Python)                                                          │
│  app/main.py  (CORS, /api router include)                                  │
│  ┌─────────────────────────── routers/ ──────────────────────────────┐    │
│  │ tree.py  tasks.py  inbox.py  dashboard.py  people.py  llm.py        │    │
│  └─────────┬──────────────────────────────────────────┬───────────────┘    │
│            │ services/                                  │ llm/               │
│            │  classification.py  risk.py  scaffold.py   │  provider.py(IF)   │
│            │                                            │  ollama.py         │
│            │  schemas.py (Pydantic I/O)                 │  heuristic.py      │
│            │  models.py (SQLModel)  db.py (engine)      │  prompts.py        │
│            ▼                                            ▼                    │
│  ┌──────────────────────┐                  ┌──────────────────────────┐     │
│  │  SQLite (ari.db)      │                  │  LLMProvider 추상화        │     │
│  │  SQLModel + Alembic   │                  │  health()/classify_      │     │
│  │  seed.py (시드 적재)   │                  │  capture()/generate_json │     │
│  └──────────────────────┘                  └────────────┬─────────────┘     │
└────────────────────────────────────────────────────────┼───────────────────┘
                                                          │ HTTP (format=json)
                                                          ▼
                                          ┌──────────────────────────────┐
                                          │  Ollama (로컬 LLM 런타임)        │
                                          │  OLLAMA_HOST=:11434            │
                                          │  OLLAMA_MODEL=<주입, 모델 비종속>│
                                          │  미가용 시 → HeuristicProvider │
                                          └──────────────────────────────┘
```

### 6.2 데이터 흐름 (대표 시나리오: 인박스 캡처)
```
[사용자 입력 "비행기 티켓 사기"]
   │ 1. POST /api/inbox/capture {kind:"text", raw:"..."}
   ▼
[FastAPI inbox.py]
   │ 2. inbox_item 생성 (status=new) → DB
   │ 3. services/classification.py 호출
   │      → LLMProvider.classify_capture(raw, context=트리/사람)
   │           ├─ Ollama 가용:  ollama.py (format=json 구조화 출력)
   │           └─ 미가용/테스트: heuristic.py (정규식 규칙 폴백)
   │ 4. inbox_classification 저장 (type/sphere/project_suggestion/reason/confidence)
   │ 5. item.status = classified
   ▼
[응답 {item, classification}]  → 프론트가 칩/이유/행선지 카드 렌더
   │ 6. (사용자가 확인) POST /api/inbox/{id}/confirm
   ▼
[type=task 이면 task 생성 → materialized_task_id 연결, status=confirmed]
   │ 7. 작업 페이지 GET /api/tasks 에 즉시 등장 (federation)
   │ 8. GET /api/risks 가 새 task 포함해 마감/쏠림/의존성 재계산
   │ 9. GET /api/dashboard 가 task_summary/inbox_recent 집계 갱신
```

---

## 7. 기술 스택 결정과 근거

> **확정 스택 — 절대 변경 금지.** (CONTRACT) 아래 표는 *근거*만 보강합니다.

| 레이어 | 선택 | 근거 |
|---|---|---|
| 프론트엔드 | **Next.js (App Router) + React + TypeScript** | 파일 기반 라우팅이 MVP 3페이지 + placeholder 그룹과 자연스럽게 매핑(`app/dashboard`, `app/inbox`, `app/tasks`, `app/(placeholder)`). TS로 `lib/types.ts` ↔ 백엔드 `schemas.py` 1:1 타입 계약. dev rewrites로 `/api` 프록시. |
| 디자인 토큰 | **CSS Variables (`styles/tokens.css`)** | 원본이 이미 `:root` CSS 변수 + `[data-theme="dark"]` 오버라이드 구조 → 무손실 이식. 프레임워크 종속 없음. |
| 테마 토글 | **next-themes** | 원본은 `document.documentElement` 의 `data-theme` + localStorage. next-themes가 동일 메커니즘(SSR 깜빡임 방지 포함)을 제공. |
| 백엔드 | **Python + FastAPI** | 타입 힌트 기반 자동 OpenAPI, Pydantic 검증으로 API 계약을 코드로 강제. LLM/분류 로직(파이썬 생태계)과 자연스럽게 결합. |
| DB | **SQLite** | 단일 파일·무설정, 로컬 완결형 MVP에 적합. 트리/중첩(`parent_id`) 인접 리스트 모델로 충분. |
| ORM | **SQLModel(SQLAlchemy) + Alembic** | SQLModel 모델 = Pydantic 스키마 기반이라 `models.py`/`schemas.py` 정합 용이. Alembic으로 스키마 진화 관리. |
| LLM | **로컬 Ollama + Provider 추상화** | 프라이버시(로컬 처리)·비용 0. **모델 비종속**: `OLLAMA_MODEL` 환경변수 주입, `LLMProvider` 인터페이스로 감싸 특정 모델 강제 금지. 미가용 시 `HeuristicProvider` 폴백으로 *오프라인/테스트에서도 분류 동작*. |
| 백엔드 테스트 | **pytest + httpx TestClient** | 라우터/서비스/분류를 결정적으로 검증. `HeuristicProvider`로 LLM 없이 골든 케이스 테스트. |
| 프론트 테스트 | **Vitest + React Testing Library**(컴포넌트), **Playwright**(E2E), **axe**(접근성) | 단위→통합→E2E→a11y 4단. 연합 흐름(캡처→확인→작업 등장)은 E2E로 검증. |
| 패키지 매니저 | 프론트 **pnpm**(또는 npm), 백엔드 **uv**(또는 pip+venv) | 빠른 설치·재현성. |

---

## 8. 리포지토리 구조

repo root: `/Users/kim1634/mycloud/personal/workspace/ari_assistant/workspace`

```
workspace/
├─ frontend/                          Next.js (App Router, TypeScript)
│  ├─ app/
│  │  ├─ layout.tsx                   앱 셸: ThemeProvider(next-themes) + <Topbar/>
│  │  ├─ page.tsx                     / → /dashboard 리다이렉트
│  │  ├─ dashboard/page.tsx           ⑤ 대시보드
│  │  ├─ inbox/page.tsx               ④ 인박스
│  │  ├─ tasks/page.tsx               ③ 작업
│  │  └─ (placeholder)/               MVP 외 10페이지 "준비 중"
│  │     └─ [slug]/page.tsx
│  ├─ components/                     디자인 시스템
│  │  ├─ Icon.tsx                     중앙 paths 맵 기반 SVG (shell.jsx P 맵 이식)
│  │  ├─ Topbar.tsx                   13항목 mainnav + 검색/알림/테마/아바타
│  │  ├─ SubRail.tsx                  좌측 아이콘 레일(선택; 작업 페이지)
│  │  ├─ GlassCard.tsx  Badge.tsx  Chip.tsx  Button.tsx
│  │  ├─ SegmentToggle.tsx            칸반/리스트 전환 (캘린더는 post-MVP placeholder)
│  │  └─ Avatar.tsx
│  ├─ lib/
│  │  ├─ api.ts                       fetch 래퍼 (/api 호출)
│  │  ├─ types.ts                     schemas.py 와 1:1 대응 타입
│  │  └─ hooks/                       useTree, useTasks, useInbox, useDashboard ...
│  ├─ styles/
│  │  ├─ tokens.css                   디자인 토큰 (dash.css :root 이식)
│  │  └─ globals.css                  body 그라데이션·폰트·전역
│  ├─ tests/                          Vitest + RTL
│  ├─ playwright/                     E2E + axe
│  ├─ next.config.js                  /api rewrites
│  ├─ tsconfig.json
│  └─ package.json
├─ backend/                           FastAPI
│  ├─ app/
│  │  ├─ main.py                      앱 생성, CORS, /api 라우터 등록
│  │  ├─ db.py                        engine/session
│  │  ├─ models.py                    SQLModel 테이블 (§9)
│  │  ├─ schemas.py                   Pydantic I/O 스키마 (types.ts 와 1:1)
│  │  ├─ routers/
│  │  │  ├─ tree.py                   폴더/프로젝트 트리
│  │  │  ├─ tasks.py                  작업 CRUD·이동·코멘트·scaffold
│  │  │  ├─ inbox.py                  캡처·분류·확인·재분류·dismiss
│  │  │  ├─ dashboard.py              집계
│  │  │  ├─ people.py                 사람
│  │  │  └─ llm.py                    LLM health
│  │  ├─ services/
│  │  │  ├─ classification.py         분류 오케스트레이션(프롬프트+폴백)
│  │  │  ├─ risk.py                   리스크 레이더(TODAY=8, tasks-risk.jsx 이식)
│  │  │  └─ scaffold.py               하위작업 제안(pickScaffold 정규식 이식)
│  │  ├─ llm/
│  │  │  ├─ provider.py               LLMProvider 추상 인터페이스
│  │  │  ├─ ollama.py                 OllamaProvider (format=json)
│  │  │  ├─ heuristic.py              HeuristicProvider (정규식 폴백)
│  │  │  └─ prompts.py                분류 프롬프트
│  │  └─ seed.py                      REF/assets 시드 적재
│  ├─ migrations/                     alembic
│  ├─ tests/                          pytest
│  └─ pyproject.toml
├─ dev/                               (이 개발 문서들 — overview.md 외 7개 phase 문서)
├─ design-reference/                  (원본 프로토타입 — 픽셀 충실 재현 기준)
└─ README.md
```

---

## 9. 데이터 모델 개요

SQLite. 필드명은 **CONTRACT 고정값** 그대로. `frontend/lib/types.ts` ↔ `backend/app/schemas.py` 필드 1:1.

### 9.1 엔티티 관계 (텍스트 ER)
```
person ──┐ (assignee)
         ▼
folder 1──* project (self ▸ parent_id 무한 중첩) 1──* task (self ▸ parent_id 무한 중첩) 1──* task_comment
                                                          │
inbox_item 1──1 inbox_classification (최신)               │
inbox_item.materialized_task_id ─────────────────────────┘ (confirm 시 연결)

대시보드 읽기전용 시드: event · approval · goal · briefing
```

### 9.2 핵심 엔티티·필드
| 엔티티 | PK | 핵심 필드 | 비고 |
|---|---|---|---|
| `person` | `id`(text, 예 `jiwoo`) | `name, initial, color, is_me(bool)` | 색은 토큰 또는 oklch 리터럴 |
| `folder` | `id`(text, 예 `work`/`life`) | `name(업무/개인), tone, icon, sort_order, is_system(bool)` | 최상위 영역(업무/개인) |
| `project` | `id`(text) | `folder_id→folder, parent_id→project(nullable), name, tone, sort_order, pinned(bool)` | 무한 중첩, `pinned`=즐겨찾기 |
| `task` | `id`(text, 예 `k1`/`kx3`) | `project_id, parent_id(nullable), title, status, assignee_id, due(date,null), prio, notes(HTML), est, delegated(bool), sort_order, created_at, updated_at` | 무한 중첩 하위작업 |
| `task_comment` | `id`(text, 예 `c1`) | `task_id, person_id, text, created_at` | |
| `inbox_item` | `id`(text, 예 `s1`) | `kind, raw, status, created_at, materialized_task_id(null)` | |
| `inbox_classification` | `id`(text, 예 `cls1`) | `inbox_item_id(1:1 최신), type, sphere, project_id(null), proj_label, tone, due_text, when_text, extra, reason(한국어), confidence(float), model, created_at` | |
| `event`(읽기전용) | `id`(text) | `time, title, tag, dur, tone, soon(bool)` | `data.js: schedule` |
| `approval`(읽기전용) | `id`(text) | `icon, tone, risk(low\|high), time, title, detail, cta(null), alt(null), undo_label(null)` | `approve-data.js: items` |
| `goal`(읽기전용) | `id`(text) | `title, pct, sub, tone` | `data.js: goals` (tone=키 값 `blue` 등, `var(--blue)`로 저장 안 함) |
| `briefing`(읽기전용) | 단일 row/settings | `today, weather, commute, sleep, note` | `data.js` (`today="6월 7일 일요일"`=히어로 날짜 라벨; 리스크 계산 `TODAY=8`(6/8)과 혼동 금지) |

### 9.3 열거형(enum) — 고정값
| enum | 값 |
|---|---|
| `task.status` (칸반 컬럼) | `todo`(할 일) · `doing`(진행 중) · `waiting`(대기 중) · `review`(검토) · `done`(완료) — *라벨·순서 고정* (`tasks-data.js: columns`) |
| `task.prio` | `높음` · `보통` · `낮음` |
| `inbox_item.kind` | `text` · `voice` · `image` |
| `inbox_item.status` | `new` · `classified` · `confirmed` · `dismissed` |
| `inbox_classification.type` | `task` · `event` · `idea` |
| `inbox_classification.sphere` | `work` · `life` |
| `tone` (전역) | `blue` · `violet` · `coral` · `green` · `amber` · `ink` · `faint` |

> 칸반 컬럼 accent(`columns`): todo=`--faint`, doing=`--blue`, waiting=`--violet`, review=`--coral`, done=`--green`.

---

## 10. REST API 개요

FastAPI, prefix `/api`. (엔드포인트·동작 고정.) 상세 요청/응답 스키마는 `phase-2-backend.md`에서 전개.

| 메서드 | 경로 | 용도 |
|---|---|---|
| GET | `/api/health` | 헬스체크 `{status:"ok"}` |
| GET | `/api/people` | `person[]` |
| GET | `/api/tree` | `folder[]` (각 folder.projects 중첩, project.task_count 포함) |
| POST | `/api/folders` | 폴더 생성 `{name,tone?,icon?}` |
| PATCH | `/api/folders/{id}` | 폴더 수정 |
| DELETE | `/api/folders/{id}` | 폴더 삭제 |
| POST | `/api/projects` | 프로젝트 생성 `{folder_id,parent_id?,name,tone?}` |
| PATCH | `/api/projects/{id}` | 프로젝트 수정 |
| DELETE | `/api/projects/{id}` | 프로젝트 삭제 |
| POST | `/api/projects/{id}/pin` | `pinned` 토글(즐겨찾기) |
| GET | `/api/tasks?area=work\|life&project_id=&status=&assignee=` | 중첩 task 트리 |
| GET | `/api/tasks/{id}` | 단건 |
| POST | `/api/tasks` | 생성 `{title,project_id,parent_id?,status?,assignee_id?,due?,prio?,notes?,est?}` |
| PATCH | `/api/tasks/{id}` | 상태 이동·필드 수정 |
| DELETE | `/api/tasks/{id}` | 삭제 |
| POST | `/api/tasks/{id}/comments` | 코멘트 `{person_id,text}` |
| POST | `/api/tasks/{id}/scaffold` | 제목 기반 하위작업 제안. body `{create?:bool=false, use_llm?:bool=false}` → `{kind, icon, items:[{title,est}], created, created_task_ids[]}` |
| GET | `/api/risks?area=work` | RiskRadar 결과(지연/쏠림/의존성), 최대 3건 |
| GET | `/api/inbox` | `inbox_item[]` (+ 최신 classification) |
| POST | `/api/inbox/capture` | `{kind, raw}` → item 생성 + 동기 분류 → `{item, classification}` |
| POST | `/api/inbox/{id}/reclassify` | 재분류 `{type?}` (타입 강제 가능) |
| POST | `/api/inbox/{id}/confirm` | 분류 결과를 작업/일정/아이디어로 *실체화*(federation) → 생성 엔티티 |
| POST | `/api/inbox/{id}/dismiss` | 무시 |
| GET | `/api/dashboard` | `{user:{name,initial}, briefing, saved_today, today_routed, schedule[], task_summary:{open_count,items[]}, goals[], approvals_summary[], inbox_recent[], badges:{appr,task,noti}}` |
| GET | `/api/llm/health` | `{reachable, model, ...}` |

### 10.1 대표 요청/응답 예시

`POST /api/inbox/capture`
```json
// 요청
{ "kind": "text", "raw": "다음 주에 한국 놀러가는 비행기 티켓 사기" }
// 응답
{
  "item": { "id": "s1", "kind": "text", "raw": "다음 주에 한국 놀러가는 비행기 티켓 사기",
            "status": "classified", "materialized_task_id": null },
  "classification": {
    "type": "task", "sphere": "life",
    "project_suggestion": { "id": null, "label": "개인 › 여행 — 한국" },
    "tone": "coral", "due_text": "출발 전 · ~6/14", "when_text": "오늘 21:00 빈 시간 추천",
    "extra": "가격 추적 알림 켜둠",
    "reason": "구매라는 행동이 있으니 '작업' 맞아요. 작업 트리의 '개인' 아래에 '여행 — 한국' 프로젝트를 만들어 넣었어요 — 따로 섹션이 생기는 게 아니라 다른 작업과 똑같이 보여요.",
    "confidence": 0.92, "model": "<OLLAMA_MODEL 또는 heuristic>"
  }
}
```

`GET /api/risks?area=work` (TODAY=8 기준, 최대 3건. 강조어는 `**굵게**` 마크다운, 식별자 필드는 `task_id`)
```json
[
  { "kind": "지연 위험", "icon": "clock", "tone": "coral", "task_id": "k1",
    "text": "**분기 리포트 초안 마무리** — 오늘(6/8) 마감인데 아직 진행 중이에요", "cta": "작업 열기" },
  { "kind": "업무 쏠림", "icon": "scale", "tone": "amber", "task_id": null,
    "text": "미완료 작업 N건이 내게 몰려 있어요 — 팀 평균의 X배. 벅찬 작업은 위임 & 추적으로 넘겨보세요", "cta": null },
  { "kind": "의존성", "icon": "link", "tone": "violet", "task_id": "...",
    "text": "**예산 섹션 작성**이(가) 늦어지면 **경영진 검토 요청 메일**까지 함께 밀려요", "cta": "후속 작업 보기" }
]
```

`GET /api/dashboard` (집계 응답 정본 — `phase-2` 소유 형태를 그대로 소비)
```json
{
  "user": { "name": "지우", "initial": "지" },
  "briefing": {
    "today": "6월 7일 일요일",
    "weather": { "temp": 24, "cond": "맑음 · 한낮 28°", "icon": "sun" },
    "commute": "출근 23분",
    "sleep": "수면 7시간 12분",
    "note": "...<b>분기 리포트</b>..."
  },
  "saved_today": "47분",
  "today_routed": 7,
  "schedule": [
    { "time": "09:30", "title": "팀 데일리 스탠드업", "tag": "프로덕트", "dur": "15분", "tone": "blue", "soon": false }
  ],
  "task_summary": {
    "open_count": 4,
    "items": [ { "id": "k1", "title": "분기 리포트 초안 마무리", "prio": "높음", "project": "분기 리포트" } ]
  },
  "goals": [ { "id": "g1", "title": "분기 OKR — 사용자 리텐션", "pct": 68, "sub": "12개 중 8개 달성", "tone": "blue" } ],
  "approvals_summary": [ { "id": "a4", "icon": "mail", "tone": "violet", "title": "현우님께 회신 초안이 준비됐어요", "time": "보내기 대기" } ],
  "inbox_recent": [ { "id": "s1", "kind": "text", "raw": "...", "type": "task", "proj_label": "개인 › 여행 — 한국", "tone": "coral" } ],
  "badges": { "appr": 3, "task": 4, "noti": 6 }
}
```
> `approvals_summary`는 `risk=="high"`만 최대 3건, 필드는 `{id,icon,tone,title,time}`(detail/cta/alt/undo_label 미포함). `task_summary`는 `{open_count, items[]}`(total/done/by_status 아님). `goals[].tone`·`schedule[].tone` 등 `tone`은 백엔드에서 **키**(`blue` 등)로 저장하고 프론트가 `var(--tone)`로 변환. `weather.icon`은 시드에 `cloudSun`으로 저장되며 응답·표시 시 `sun`으로 매핑(R11 참조). 사용자 인사("좋은 아침이에요, {name}님")는 `response.user.name` 사용(하드코딩 금지).

---

## 11. 디자인 시스템 요약

기준 파일: `REF/assets/dash.css` `:root`. → `frontend/styles/tokens.css`로 무손실 이식. body 전역은 `globals.css`.

### 11.1 토큰 카테고리 (HEX 그대로)
| 카테고리 | 토큰 = 값 |
|---|---|
| 배경 | `--bg-top #f6efe7` · `--bg-mid #eef0f1` · `--bg-bot #e9ebed` (body: `linear-gradient(178deg, top 0%, mid 20%, bot 100%)`, `background-attachment: fixed`) |
| 카드 | `--card #ffffff` · `--card-2 #f4f5f6` · `--card-3 #eef0f1` |
| 잉크 | `--ink #211f1c` · `--ink-2 #514d47` · `--muted #8d8a85` · `--faint #b6b3ad` |
| 라인 | `--line rgba(33,31,28,.07)` · `--line-2 rgba(33,31,28,.13)` |
| 채움(선택) | `--fill #29241f` · `--fill-soft #36302a` · `--on-fill #f4efe6` |
| CTA 라임 | `--lime #c2f24a` · `--lime-hi #cdf85e` · `--lime-ink #233006` |
| 액센트 | `--blue #4f72e0` · `--coral #df7256` · `--green #4e9b66` · `--violet #8b6fd4` · `--amber #e0a23c` |
| 라운드 | `--radius 22px` · `--radius-sm 14px` |
| 그림자 | `--shadow 0 10px 30px -14px rgba(30,26,22,.22), 0 2px 6px -2px rgba(30,26,22,.06)` · `--shadow-sm 0 4px 14px -8px rgba(30,26,22,.18)` |
| 글래스 | `--glass rgba(255,255,255,.4)` · `--glass-2 rgba(255,255,255,.26)` · `--glass-brd rgba(255,255,255,.7)` · `--glass-ink rgba(26,22,18,.46)` · `--blur blur(26px) saturate(190%)` · `--glass-hi inset 0 1px 0 rgba(255,255,255,.75)` |

### 11.2 폰트
- `--font-disp: "Onest", "Pretendard", system-ui` (디스플레이/제목)
- `--font-ui: "Pretendard", "Onest", system-ui` (본문 기본)
- `--font-mono: "DM Mono", ui-monospace` (숫자/영문, `.mono { font-variant-numeric: tabular-nums }`)
- 한국어 가독성: `body { word-break: keep-all; letter-spacing: -0.011em; }`
- 로드: Onest+DM Mono(Google Fonts), Pretendard(jsDelivr CDN) — `작업.html` `<head>` 참조.

### 11.3 라이트/다크
- 메커니즘: `documentElement` 의 `data-theme="light|dark"` 속성 + localStorage 영속 → **next-themes** 사용.
- 다크 오버라이드(`[data-theme="dark"]`): `--bg-top #201d1a` · `--card #2c2925` · `--ink #f3eee6` · `--fill #f4efe6`(반전) 등. 전체 값은 `dash.css` 54~77행 그대로 이식.
- 토글 UI: 상단바 `달/해` 아이콘 버튼(`Icon name="moon"|"sun"`, `shell.jsx` Topbar).

### 11.4 글래스
반투명 카드 = `background: var(--glass)` + `backdrop-filter: var(--blur)` + `border: 1px solid var(--glass-brd)` + `box-shadow: var(--glass-hi)`. 상단바는 `backdrop-filter: blur(14px) saturate(150%)`, sticky, height 70px.

### 11.5 아이콘 패턴
- 원본: 인라인 SVG **path 딕셔너리 `P`** + `<Icon d="name"/>`가 path 문자열을 `"|"`로 split해 다중 `<path>` 렌더(`shell.jsx` 42~49행).
- 이식: `frontend/components/Icon.tsx` — **중앙 `paths` 맵** + `<Icon name="..."/>`. `shell.jsx`의 `P` 맵 **전체**(spark/grid/route/cal/check/tick/mail/target/heart/wallet/brain/zap/more/search/bell/back/send/sun/moon/swap/up/arrow/inbox/mic/image/pen/users/clock/play/plus/shield/pin)를 옮긴다. 리스크 전용 아이콘(`tasks-risk.jsx` `RP`: radar/clock/scale/link/chev)도 통합.
- 공통 SVG 속성: `viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth={1.9} strokeLinecap/Linejoin="round"`, `.ic { width:1em; height:1em }`.
- **누락 시 렌더 깨짐** 주의(원본 개발 메모) — paths 맵은 단일 출처로 통합하고 누락 키 가드.

### 11.6 공유 컴포넌트
| 컴포넌트 | 역할 | 원본 |
|---|---|---|
| `Icon(name)` | 중앙 paths 맵 SVG | `shell.jsx` `Icon` |
| `Topbar(current)` | 좌 브랜드(아리/AI LIFE OS) · 중앙 13항목 mainnav · 우 검색/알림(dot)/테마/아바타. active=글래스 pill | `shell.jsx` `Topbar`+`MAIN`, `dash.css` `.topbar/.mainnav/.top-actions/.t-btn/.t-ava` |
| `SubRail(items,active,onPick)` | 좌측 아이콘 레일(아이콘+툴팁, dot 배지, sep). MVP 작업 페이지 선택적 사용 | `shell.jsx` `SubRail` |
| `GlassCard, Badge, Chip, Button(라임 CTA), SegmentToggle, Avatar` | 프리미티브. SegmentToggle=칸반/리스트(캘린더는 post-MVP placeholder) | — |

---

## 12. 페이지 연합(federation) 흐름

아리의 핵심 가치는 화면 간 **데이터를 주고받는 하나의 흐름**입니다(PROJECT-README §5). MVP에서 구현하는 연합:

### 12.1 캡처 → 분류 → 작업 (메인 루프)
```
인박스: 한 줄 입력
   → POST /api/inbox/capture (동기 분류, classification.py)
   → 결과 칩(type/sphere)·이유(reason)·행선지 카드(proj_label) 표시
   → POST /api/inbox/{id}/confirm
       └ type=task → task 생성 + materialized_task_id 연결
   → 작업 페이지 GET /api/tasks 에 "다른 작업과 똑같이" 등장 (개인=프로젝트 원칙)
```
시드 예: `s1`(비행기 티켓) → 작업 `k20` 한국행 비행기 티켓 구매(`life-trip`)로 실체화된 모습이 이미 트리에 존재(`k20.notes`: *"스마트 인박스에서 자동 생성된 작업이에요"*).

### 12.2 리스크 레이더 = 작업 데이터로 계산
`GET /api/risks`는 **현재 작업 트리를 직접 계산**(별도 저장 없음, `risk.py`, `TODAY=8`):
- **지연 위험**: `status≠done && due 일자 ≤ 8` → 1건. 예) `k1` 분기 리포트(due 06-08, doing).
- **업무 쏠림**: 담당자별 미완료 수 집계 → 최다 담당자가 `평균×1.5 이상 && 4건 이상`이면 경고. `me`면 *위임 & 추적* CTA, 타인이면 *재배분* 권유.
- **의존성**: 제목 기반 DEPS 매칭(`예산 섹션 작성→경영진 검토 요청 메일`, `데이터 전처리 파이프라인→사용자 인터뷰 5건 정리`). 둘 다 미완료면 1건. 최대 3건 반환.

### 12.3 대시보드 = 집계 화면
`GET /api/dashboard`가 다른 데이터를 읽어 집계(그래서 **마지막에 빌드**):
- `task_summary` ← tasks, `inbox_recent` ← inbox_item, `schedule`/`approvals_summary` ← 읽기전용 시드, `briefing`/`goals` ← 시드, `badges` ← 카운트.
- 따라서 페이지 빌드 순서는 **작업 → 인박스 → 대시보드**(§13 빌드 순서와 일치).

---

## 13. 개발 Phase 로드맵

### 13.1 로드맵 표
| Phase | 문서 | 목표(끝나면 동작) | 주요 산출물 | 의존성 | 예상 작업량 |
|---|---|---|---|---|---|
| 0 | `phase-0-foundation.md` | 모노레포 스캐폴딩·개발환경. Next dev + FastAPI `/api/health` + SQLite + Ollama 연결 확인 | `frontend/`·`backend/` 골격, `next.config` rewrites, `db.py`, `/api/health`, `/api/llm/health` | overview | S |
| 1 | `phase-1-design-system.md` | 토큰·앱 셸. Topbar 13항목·테마 토글·Icon·라우팅·placeholder 화면 | `tokens.css`, `globals.css`, `Icon/Topbar/SubRail/GlassCard/...`, `(placeholder)` 라우트 | 0 | M |
| 2 | `phase-2-backend.md` | 데이터모델·마이그레이션·시드·전 REST API·Ollama 추상화·분류/리스크/scaffold 서비스 | `models.py`, alembic, `seed.py`, routers 6, services 3, llm 4, `schemas.py` | 0 | L |
| 3 | `phase-3-tasks.md` | 작업 페이지: 폴더 트리 사이드바, 칸반/리스트(캘린더는 placeholder), 하위작업, 상세 드로어, 리스크 레이더 | `app/tasks/page.tsx`, 트리/칸반/드로어 컴포넌트, `useTasks/useTree/useRisks` | 1,2 | L |
| 4 | `phase-4-inbox.md` | 인박스: 캡처 컴포저, 실시간 분류, 결과 칩/이유, 확인·재분류, 행선지 카드, 작업 실체화 | `app/inbox/page.tsx`, 컴포저/분류결과/행선지 컴포넌트, `useInbox` | 1,2,3 | M |
| 5 | `phase-5-dashboard.md` | 대시보드: 아침 브리핑, 결재함/인박스 요약, 일정/작업/목표 요약, 자연어 명령 입력 | `app/dashboard/page.tsx`, 브리핑/요약 카드, `useDashboard` | 1,2,3,4 | M |
| 6 | `phase-6-integration.md` | 연합 흐름 통합·E2E·접근성·성능·실행법·MVP 수용 기준 | Playwright E2E, axe, 실행 스크립트, 수용 체크리스트 | 0~5 | M |

> 작업량 표기: S(작음)/M(보통)/L(큼) — 상대 추정.

### 13.2 빌드 순서
```
0 → 1 → 2 → ( 3 → 4 → 5 ) → 6
                작업  인박스  대시보드
```
페이지는 **작업 → 인박스 → 대시보드** 순(대시보드가 작업·인박스 데이터를 집계하므로 마지막, §12.3).

---

## 14. LLM(Ollama) 추상화 & 분류 계약

### 14.1 Provider 추상화 (`backend/app/llm/`)
```python
# provider.py — 추상 인터페이스 (특정 모델 비종속)
class LLMProvider(ABC):
    def health(self) -> dict: ...                       # {reachable, model, ...}
    def generate_json(self, prompt: str, schema: dict) -> dict: ...
    def classify_capture(self, raw: str, context: dict) -> Classification: ...
```
| 구현 | 파일 | 설명 |
|---|---|---|
| `OllamaProvider` | `ollama.py` | Ollama HTTP API(`/api/chat` 또는 `/api/generate`)에 `format=json` 구조화 출력. 설정 `OLLAMA_HOST`(기본 `http://localhost:11434`), `OLLAMA_MODEL`(주입). **모델 비종속** — 문서·코드에서 특정 모델 강제 금지. |
| `HeuristicProvider` | `heuristic.py` | LLM 미가용/오프라인/테스트용 **규칙 기반 폴백**. 시간표현 정규식→`event`, 행동동사+기한→`task`, 막연→`idea`. `tasks-data.js` `pickScaffold` 정규식 패턴을 분류 보조에 참고. |

선택 로직: `OllamaProvider.health().reachable`이 false면 자동으로 `HeuristicProvider`로 폴백 → **오프라인/CI에서도 분류가 동작**.

### 14.2 분류 결과(Classification) 스키마 = `inbox_classification` 필드
```ts
type Classification = {
  type: "task" | "event" | "idea";
  sphere: "work" | "life";
  project_suggestion: { id?: string; label: string };  // 예 "개인 › 여행 — 한국"
  due_text?: string; when_text?: string; extra?: string;
  reason: string;          // 사람이 읽는 한국어 한두 문장
  confidence: number;      // 0~1
};
```

### 14.3 분류 규칙 (프롬프트 + 폴백 공통)
1. **시간 명시 → `event`**(일정). **행동(동사)+기한 → `task`**(작업). **막연/미정 → `idea`**(아이디어).
2. **sphere**: 업무 키워드(미팅/리포트/온보딩/OKR/리뷰…) → `work`, 개인 키워드(가족/여행/선물/구독/병원…) → `life`.
3. **project_suggestion**: 기존 프로젝트 트리 매칭, 없으면 적절한 folder 아래 새 프로젝트 제안(예: 비행기 티켓 → `개인 › 여행 — 한국`).
4. **reason**: 사람이 읽는 한국어 1~2문장. 개인=프로젝트 철학을 말로 설명(예: *"따로 섹션이 생기는 게 아니라 다른 작업과 똑같이 보여요"*).

### 14.4 골든 분류 테스트 케이스 (반드시 테스트에 포함, = 인박스 시드 `s1~s4`)
| 입력(raw) | type | sphere | project_suggestion | 비고 |
|---|---|---|---|---|
| 다음 주에 한국 놀러가는 비행기 티켓 사기 | task | life | 개인 › 여행 — 한국 | `extra` 가격추적 |
| 수요일 11시 자전거 수리 맡기기 | event | life | 개인 캘린더 | when 수 11:00 |
| 엄마 생신 선물 미리 알아보기 | task | life | 가족 | 주말 블록 |
| 온보딩 환영 화면에 짧은 애니메이션 넣으면 어떨까 | idea | work | 온보딩 리디자인 · 아이디어 보드 | 보드 보관 |

---

## 15. 상태 처리 & 엣지 케이스 (전역 원칙)

각 페이지 phase 문서가 세부를 다루되, 전역 원칙은 다음과 같습니다.

| 상태 | 전역 처리 |
|---|---|
| **로딩** | 글래스 스켈레톤(카드 형태 유지). 데이터 hook(`lib/hooks/`)의 `isLoading`. |
| **빈(empty)** | 인박스 빈: *"적을 때는 분류하지 않아요 — 떠오른 걸 그냥 적어보세요"* 류 안내. 작업 빈 컬럼: 컬럼 라벨 유지 + placeholder. |
| **에러** | `lib/api.ts`가 비2xx를 throw → 카드 단위 에러 + 재시도 버튼. 전체 앱 셸(Topbar/테마)은 유지. |
| **오프라인/LLM 미가용** | `GET /api/llm/health.reachable=false` → 분류는 `HeuristicProvider`로 계속 동작, `classification.model="heuristic"` 표기. UI는 "아리가 규칙 기반으로 분류했어요" 류 미세 힌트(선택). |
| **HTML 시드(notes/briefingNote)** | 신뢰된 시드지만 사용자 입력 경로(`task.notes`)는 새니타이즈(예: DOMPurify) 후 렌더. |
| **무한 중첩** | project/task `parent_id` 깊이 무제한 — 렌더는 들여쓰기(depth), API는 중첩 트리 반환. 깊이 폭주 가드 권장. |
| **삭제 연쇄** | folder/project 삭제 시 하위 처리 정책은 `phase-2-backend.md`에서 확정(이 문서는 엔드포인트만 정의). |

---

## 16. 테스팅 & 검증

> 본 문서 자체는 *지도*이므로 "검증" = **문서 정확성 + 각 phase로의 정합성**을 점검하는 것입니다. 동시에, 전 phase가 공통으로 쓸 **테스트 도구·실행 명령·통과 기준**을 여기서 한 번에 정의합니다(각 phase 문서는 이를 재사용).

### 16.1 실행 명령 (전 phase 공통 레퍼런스)
```bash
# 백엔드 (backend/)
uv run pytest                          # 또는: pytest
uv run pytest -k "classify"            # 골든 분류 케이스만
uv run uvicorn app.main:app --reload   # 개발 서버 (:8000)
uv run alembic upgrade head            # 마이그레이션
uv run python -m app.seed              # 시드 적재

# 프론트엔드 (frontend/)
pnpm dev                               # Next dev (:3000, /api → :8000 rewrite)
pnpm test                              # Vitest + React Testing Library (컴포넌트)
pnpm playwright test                   # Playwright E2E
pnpm playwright test --grep @a11y      # axe 접근성 (태그 기반, 예시)
pnpm build && pnpm start               # 프로덕션 빌드 검증
```

### 16.2 이 문서(overview)의 검증 케이스
| # | 검증 항목 | 통과 기준 |
|---|---|---|
| O-1 | 토큰 값 일치 | §11 표의 모든 HEX/px가 `REF/assets/dash.css` `:root`와 100% 일치 |
| O-2 | people 일치 | §4.2 5인의 id/name/initial/color/is_me가 `tasks-data.js: people`와 일치(수아 oklch 포함) |
| O-3 | enum 일치 | §9.3 모든 enum 값·라벨·순서가 CONTRACT/`columns`와 일치 |
| O-4 | API 표 완전성 | §10 표가 CONTRACT의 24개 엔드포인트를 빠짐없이 포함 |
| O-5 | 골든 케이스 = 시드 | §14.4 4건이 `sinbox-data.js: items` s1~s4와 type/sphere/proj 일치 |
| O-6 | TODAY 구분 | §4.3에 `today="6월 7일 일요일"` vs 리스크 `TODAY=8` 차이가 명시 |
| O-7 | 빌드 순서 | §13.2가 `0→1→2→(3→4→5)→6`, 페이지 작업→인박스→대시보드 |
| O-8 | 상호 참조 | 모든 phase 문서가 정확한 파일명(`phase-N-*.md`)으로 링크됨 |
| O-9 | 13항목 내비 | §5.1/§11.6의 MAIN 항목·badge(결재함3/작업4/알림6)가 `shell.jsx: MAIN`과 일치 |

### 16.3 전 phase 공통 통과 기준(요약, 상세는 각 문서)
- 백엔드: `pytest` 전건 통과, 골든 분류 4케이스 통과(LLM 없이 `HeuristicProvider`로도), `GET /api/risks`가 TODAY=8로 지연/쏠림/의존성 최대 3건 정확.
- 프론트: `pnpm test` 컴포넌트 통과, `pnpm playwright test` 연합 E2E(캡처→확인→작업 등장) 통과, axe 위반 0(심각도 serious 이상).
- 디자인 충실도: 라이트/다크 토큰 적용, 상단바 13항목·테마 토글 동작, 글래스/라운드/그림자가 토큰과 일치.

### 16.4 수동 QA 체크리스트 (MVP 전체)
- [ ] `/` 진입 시 `/dashboard`로 리다이렉트.
- [ ] 상단 내비 13항목 모두 표시, MVP 외 10항목 클릭 시 "준비 중" 화면(셸 유지).
- [ ] 테마 토글(달/해) 동작 + 새로고침 후 유지(localStorage).
- [ ] 인박스에 4개 골든 문장 입력 → 각각 task/event/idea·work/life·올바른 행선지·한국어 이유 표시.
- [ ] 인박스 작업형 항목 확인 → 작업 페이지 트리에 등장.
- [ ] 작업 페이지 칸반 5컬럼(할 일/진행 중/대기 중/검토/완료) 라벨·순서.
- [ ] 리스크 레이더에 지연(분기 리포트)·쏠림·의존성 경고 표시.
- [ ] 대시보드 아침 브리핑·일정 4건·목표 3개·결재함/인박스 요약 표시.
- [ ] 한국어 줄바꿈 `word-break: keep-all` 적용(어절 단위 줄바꿈).

---

## 17. 완료 기준 (Definition of Done)

이 진입 문서의 DoD:
- [x] 제품 비전·3대 철학(적을 때 분류 안 함 / 이미 해뒀어요 / 개인=프로젝트) 서술.
- [x] 페르소나(지우)·팀원 5인·6/7~12 한 주·TODAY=8 명시.
- [x] MVP 범위(작업/인박스/대시보드)와 13페이지 경계·placeholder 규칙.
- [x] 텍스트 아키텍처 다이어그램(브라우저↔FastAPI↔SQLite↔Ollama)·데이터 흐름.
- [x] 스택 결정·근거표.
- [x] 모노레포 전체 트리.
- [x] 데이터 모델(ER·필드·enum) 표.
- [x] REST API 24엔드포인트 표 + 요청/응답 예시.
- [x] 디자인 토큰(HEX/px)·폰트·라이트/다크·글래스·아이콘 패턴.
- [x] 연합 흐름(캡처→분류→작업, 리스크=작업계산, 대시보드 집계).
- [x] phase 0~6 로드맵 표 + 빌드 순서.
- [x] LLM Provider 추상화 + 분류 스키마/규칙/골든 케이스.
- [x] 용어집·문서 세트 사용법.
- [x] 모든 phase 문서를 정확한 파일명으로 상호 참조.

---

## 18. 용어집

| 용어 | 정의 |
|---|---|
| **인박스(Inbox)** | "일단 적으세요"의 진입점. 텍스트/음성/이미지 캡처(`inbox_item`)를 받아 아리가 분류. MVP 핵심 페이지. |
| **캡처(Capture)** | 인박스에 던진 한 줄/메모. `POST /api/inbox/capture`로 생성 + 동기 분류. |
| **분류(Classification)** | 캡처를 `task/event/idea` + `work/life` + 행선지 프로젝트 + 한국어 `reason`으로 판정한 결과(`inbox_classification`). |
| **실체화(Materialize / federation)** | 분류 결과를 실제 엔티티로 확정. `confirm` 시 task 생성 + `materialized_task_id` 연결 → 작업 페이지에 등장. |
| **결재함(Approvals)** | 아리가 "이미 해둔" 자동 처리를 모아 승인/되돌리기(`approval`). MVP는 대시보드 요약 카드로만 읽기전용 노출. risk=low(되돌리기)/high(확인 후 실행). |
| **리스크 레이더(Risk Radar)** | 작업 트리를 직접 계산해 **지연 위험·업무 쏠림·의존성**을 최대 3건 경고(`GET /api/risks`, TODAY=8). |
| **sphere** | 영역 구분 `work`(업무) / `life`(개인). "개인=프로젝트" 원칙상 별도 섹션이 아니라 *필터*. |
| **folder(영역)** | 트리 최상위(업무/개인). 사용자 생성 가능, `is_system`으로 시스템 폴더 구분. |
| **project** | folder 아래 무한 중첩 가능한 프로젝트. `pinned`=즐겨찾기. |
| **task / 하위작업** | 작업. `parent_id`로 무한 중첩(하위작업도 그 자체로 완전한 작업). status 5단(칸반). |
| **scaffold(자동 쪼개기)** | 작업 제목 기반 하위작업 제안(`POST /api/tasks/{id}/scaffold`). `pickScaffold` 정규식으로 research/dev/doc/generic 템플릿 선택. |
| **tone** | 색 토큰 집합 `blue/violet/coral/green/amber/ink/faint`. 카드 점·바·라벨 액센트. |
| **prio(우선순위)** | `높음/보통/낮음`. |
| **브리핑(Briefing)** | 대시보드 아침 요약(날씨/출근/수면/노트). `data.js`. |
| **글래스(Glass)** | 반투명 카드 스타일(`--glass*` + `--blur`). 클린 화이트 글래스 테마의 정체성. |
| **Provider 추상화** | LLM 교체 가능 인터페이스(`LLMProvider`). `OllamaProvider`(로컬) ↔ `HeuristicProvider`(폴백). 모델 비종속(`OLLAMA_MODEL`). |
| **SubRail** | 좌측 아이콘 레일(아이콘+툴팁+dot+sep). MVP는 작업 페이지에서 선택적 사용. |

---

## 19. 이 문서 세트 사용법

### 19.1 읽는 순서
1. **`overview.md`(이 문서)** — 전체 그림·계약·로드맵. (현재)
2. **`phase-0-foundation.md`** — 모노레포 스캐폴딩 & 개발환경(Next + FastAPI + SQLite + Ollama 연결 확인).
3. **`phase-1-design-system.md`** — 디자인 토큰 이식 + 앱 셸(Topbar/테마/Icon/레이아웃/placeholder 라우팅).
4. **`phase-2-backend.md`** — 데이터모델·마이그레이션·시드·REST API·Ollama 추상화·분류/리스크/scaffold 서비스.
5. **`phase-3-tasks.md`** — 작업 페이지(트리 사이드바·칸반/리스트[캘린더는 placeholder]·하위작업·상세 드로어·리스크 레이더).
6. **`phase-4-inbox.md`** — 인박스(캡처 컴포저·실시간 분류·칩/이유·확인/재분류·행선지·작업 실체화).
7. **`phase-5-dashboard.md`** — 대시보드(아침 브리핑·결재함/인박스 요약·일정/작업/목표 요약·자연어 명령 입력).
8. **`phase-6-integration.md`** — 연합 통합·E2E·접근성·성능·실행법·MVP 수용 기준.

### 19.2 작업 방식
- 각 phase 문서는 **개요/선행·산출물/상세구현/데이터·타입·API/디자인 충실도/상태·엣지/테스팅·검증/DoD/다음 단계** 9개 섹션을 가집니다.
- 항상 **빌드 순서(§13.2)** 를 지키세요: `0→1→2→(3→4→5)→6`.
- 값(색·필드·문구)은 항상 **`REF/assets`의 원본을 단일 출처**로 삼고, 의심되면 이 문서의 표와 대조하세요.
- 계약(데이터 모델·API·토큰·스택·명명)은 **변경 금지** — 변경이 필요하면 먼저 이 `overview.md`를 갱신하고 전 phase에 전파합니다.

---

*끝. 다음 문서: `phase-0-foundation.md`*
