vignette/AGENTS.md
2026-06-28 21:50:21 +09:00

167 lines
13 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.

# AGENTS.md — Vignette 프로젝트 작업 지침
> Vignette = AI 심리상담 시뮬레이션 훈련 플랫폼 (한신대 산학협력).
> 모노레포: `apps/api`(FastAPI/Python), `apps/web`(React 19/Vite/Playwright), `docs`, `infra`, `scripts`.
## 📚 문서 맵 — 작업 전 해당 가이드를 먼저 읽어라
| 목적 | 문서 |
|---|---|
| 저장소 개요·빠른 시작 | [`README.md`](./README.md) |
| **로컬 서버 띄우기·테스트** | [`docs/guides/local-development.md`](./docs/guides/local-development.md) |
| 시스템 아키텍처·데이터 흐름 | [`docs/guides/architecture.md`](./docs/guides/architecture.md) |
| 테스트·검증 실행 | [`docs/guides/testing.md`](./docs/guides/testing.md) |
| 원천문서·갭 로드맵 | [`docs/guides/source-docs-and-gaps.md`](./docs/guides/source-docs-and-gaps.md) |
| **SSOT 상태판** | [`docs/dev_dashboard.html`](./docs/dev_dashboard.html) |
| 백로그 | [`docs/ops/backlog-2026-06-26.md`](./docs/ops/backlog-2026-06-26.md) |
| **서연 아바타 핸드오프** | [`docs/ops/handoff-avatar-seoyeon-2026-06-27.md`](./docs/ops/handoff-avatar-seoyeon-2026-06-27.md) |
작업 결과로 동작/구조가 바뀌면 해당 가이드와 SSOT 대시보드를 함께 갱신한다.
---
## ⚠️ 0. 무조건 OS를 먼저 파악하고 시작한다 (최우선·필수)
**어떤 작업이든 명령을 실행하기 전에 OS와 셸을 먼저 확정하라.** 이걸 건너뛰면
경로/인코딩/도구 차이로 시간을 크게 낭비한다(실제로 그랬다).
작업 시작 시 반드시 확인할 것:
1. **OS / 셸**: 이 저장소의 주 개발 환경은 **Windows 11 + PowerShell**이다.
POSIX를 가정하지 마라. Bash 도구도 쓸 수 있으나 셸마다 문법이 다르다.
2. **경로 규칙**: Windows 절대경로(`D:\...`, `C:\...`). 한글·공백 포함 경로가 흔하다
(예: OneDrive `문서\카카오톡 받은 파일`). `-LiteralPath`로 다루고, 외부 도구에
넘기기 전에 **ASCII 이름으로 로컬 복사**해 인코딩/공백 문제를 차단하라.
3. **PowerShell 판(5.1) 주의**: 인라인 `if(){}else{}`를 식으로 못 쓴다(삼항 없음).
네이티브 exe stderr를 `2>&1`로 합치지 마라(ErrorRecord로 감싸짐).
기본 출력 인코딩은 UTF-16 — 다른 도구가 읽을 파일은 `-Encoding utf8`.
4. **외부 CLI는 실제로 블로킹되는지 확인**: GUI 런처(`soffice.exe` 등)는 즉시
detach되어 `Start-Process -Wait`가 변환을 안 기다린다. 실제 작업 프로세스
(`soffice.bin`)를 직접 호출하라. 좀비 프로세스가 락을 잡으면 정리부터 한다.
5. **도구 가용성 먼저 탐지**: 변환/처리 전에 LibreOffice·pandoc·python 라이브러리·
Playwright 브라우저 등 무엇이 설치돼 있는지 먼저 확인하고 경로를 잡아라.
> 한 줄 요약: **"먼저 OS·셸·경로·도구를 확정한 뒤 실행한다."** 추정 금지.
### 0.1 PowerShell 5.1 실행 규칙 (Windows 기본 셸)
이 프로젝트의 기본 셸은 **Windows PowerShell 5.1 Desktop**이다. PowerShell 7 문법이나
Bash 문법을 섞으면 바로 지연된다. 명령을 작성할 때 아래 규칙을 기본값으로 삼아라.
- **세션 시작 프리루드**: 한글/UTF-8 출력이 필요한 명령 전에는 아래를 먼저 둔다.
```powershell
$ErrorActionPreference = 'Stop'
[Console]::OutputEncoding = [System.Text.UTF8Encoding]::new($false)
$OutputEncoding = [System.Text.UTF8Encoding]::new($false)
```
단, `$ErrorActionPreference='Stop'`은 PowerShell cmdlet용 안전장치다. 네이티브 exe의
실패는 자동으로 예외가 되지 않으므로 실행 후 `$LASTEXITCODE`를 반드시 확인한다.
- **5.1 미지원 문법 금지**: `? :` 삼항, `??`, `??=`, `&&`, `||`,
`ForEach-Object -Parallel`, Bash heredoc(`<<EOF`), Bash식 환경변수 주입
(`FOO=bar command`)을 쓰지 않는다. 값 선택은 명시적 `if/else`와 변수 대입으로 쓴다.
- **제어문은 파이프라인 값이 아니다**: `foreach (...) { ... } | Format-Table`처럼 쓰면
5.1에서 파서 오류가 난다. 필요하면 `& { foreach (...) { ... } } | Format-Table`처럼
스크립트블록을 파이프라인 입력으로 감싼다.
- **경로는 PowerShell 방식으로 다룬다**: 파일/폴더에는 `-LiteralPath`,
`Resolve-Path -LiteralPath`, `Join-Path`를 우선 사용한다. 한글·공백·괄호가 있는 경로를
외부 CLI에 직접 넘기기 전에는 ASCII 임시 경로로 복사하는 편이 낫다.
- **파일 인코딩을 명시한다**: `Get-Content`/`Set-Content`/`Out-File`에는 필요한 경우
`-Encoding UTF8`을 붙인다. 단, Windows PowerShell 5.1의 `-Encoding UTF8`은 BOM을 쓴다.
Node/Python/TS 도구가 읽을 **UTF-8 no BOM** 파일은 .NET API로 쓴다.
```powershell
[IO.File]::WriteAllText($path, $text, [Text.UTF8Encoding]::new($false))
```
- **한글 출력 깨짐은 파일 손상으로 단정하지 않는다**: 먼저 `OutputEncoding`을 UTF-8로
맞추고, 필요하면 `Format-Hex`, Node/Python 읽기, 실제 빌드/타입체크로 확인한다.
- **네이티브 exe stderr를 `2>&1`로 합치지 않는다**: 5.1은 네이티브 stderr를
`ErrorRecord`로 감싸 파이프라인/문자열 처리와 순서를 흐릴 수 있다. 로그가 필요하면
stdout/stderr를 별도 파일로 리디렉션하거나 `System.Diagnostics.Process`로 분리 캡처한다.
- **네이티브 명령은 문자열 조립보다 인자 배열로 호출한다**:
```powershell
$exe = 'C:\path\tool.exe'
$args = @('--flag', $value, '--out', $outPath)
& $exe @args
if ($LASTEXITCODE -ne 0) { throw "tool failed: $LASTEXITCODE" }
```
PowerShell 파싱이 외부 도구 인자를 망가뜨릴 때만 네이티브 명령 뒤에 `--%`를 검토한다.
- **HTTP/JSON은 `curl` 별칭을 피한다**: PowerShell의 `curl`은 별칭일 수 있다.
JSON API는 `Invoke-RestMethod`/`Invoke-WebRequest`와 `ConvertTo-Json`을 우선 사용하고,
진짜 curl이 필요하면 `curl.exe`를 명시한다.
- **인라인 Python/Node는 짧고 결정적으로 실행한다**: 여러 줄 코드를 stdin으로 밀어 넣다
BOM/인용 문제가 나면 `python -c`, UTF-8 no BOM 임시 파일, 또는 base64 전달을 쓴다.
Python 검증에는 필요 시 `$env:PYTHONUTF8='1'`와 `python -X utf8`을 사용한다.
- **`powershell.exe -EncodedCommand`는 UTF-16LE base64**다. UTF-8로 인코딩하면 깨진다.
- **프로세스 대기는 검증한다**: 단순 CLI는 직접 실행하고 `$LASTEXITCODE`를 본다.
`Start-Process`가 필요하면 `-Wait -PassThru`로 ExitCode를 확인한다. GUI 런처가 즉시
detach되는 도구(예: LibreOffice `soffice.exe`)는 실제 작업 프로세스와 산출물 생성을
따로 검증한다.
---
## 1. 운영 원칙
- **에이전트 지침의 원본은 이 파일(`AGENTS.md`) 하나다.** `CLAUDE.md`, `AGENT.md`처럼
도구별로 자동 탐지되는 파일은 호환성 진입점으로만 유지한다. 프로젝트 규칙을 바꿀 때는
이 파일만 수정하고, 진입점 파일에는 중복 규칙을 추가하지 않는다.
- **가짜 증거로 DONE 표기 금지.** 실증/외부 의존/소유자 결정이 필요한 항목은
`docs/ops/backlog-*.md`에 분류해 추적한다(B1 코스메틱 · B2 환경제약 · B3 소유자결정 · B4 외부거버넌스).
- **`docs/dev_dashboard.html`이 SSOT(단일 진실 공급원)다.** 상태·검증 증거·결정 필요·로드맵의 권위 기준이며, 새 발견·작업 결과·상태 변경은 별도 문서로만 남기지 말고 대시보드에 반영/동기화한다. 백로그(`docs/ops/backlog-*.md`)는 대시보드와 일치시킨다(어긋나면 대시보드 기준).
- 소유자(윤찬) 단독 결정 사안을 임의로 정하지 않는다(월권 금지).
## 2. 검증 기준 (프론트 변경 시)
- `cd apps/web && npm run typecheck`
- 레이아웃 변경은 `e2e/layout-visual-gate.spec.ts`(7/7) + 레이아웃 포커스 E2E +
`e2e/session-layout.spec.ts`(8/8) 무회귀. E2E는 web+api(+DB) 스택이 떠 있어야 한다.
## 3. 커뮤니케이션
- 모든 대화·주석·커밋 메시지는 한글.
- git 커밋 메시지에 Co-Authored-By / Codex 관련 문구 추가 금지.
---
## 4. 이미지 생성(gpt-image-2 = imagegen2) · Live2D식 아바타
> "이미지 생성해/만들어/그려줘" 요청 → `~/.Codex/skills/codex-image` 스킬이 아래 래퍼를 자동 사용.
### 4.1 gpt-image-2 호출 (반드시 래퍼)
```bash
bash ~/.codex/imagegen-headless/codex_imagegen.sh \
--out <경로.png> [--size WxH] [--quality low|medium|high|auto] \
[-i <참조이미지> ...] [--all] "<프롬프트>"
```
- 인증: ChatGPT 구독 OAuth(`~/.codex/auth.json`의 `auth_mode=="chatgpt"`). **API 키 사용 금지**(과금).
- codex 0.140+는 생성 이미지를 세션 rollout JSONL에 **base64로 인라인 반환** → 래퍼의 `extract_imagegen.py` 추출만 결정적. stdout의 "저장 경로"는 **환각**(직접 codex exec 금지).
- 프롬프트는 **stdin 파이프**로(인자 전달 시 멈춤). 변주 생성 시 base를 `-i` 참조로 넘겨 아이덴티티·프레이밍 고정.
- 투명배경 미지원 → 단색 평면 배경으로 생성 후 누끼. 한글 텍스트 렌더 가능(stdin 파이프라 인코딩 문제 없음).
### 4.2 누끼(컷아웃)
- `object-separation` 스킬(BiRefNet): `~/.venvs/object-separation/Scripts/python.exe ~/.agents/skills/object-separation/scripts/separate_object.py <in> <out> --model birefnet-general`
- 알파 정제(잔류 헤이즈 제거): 임계치 `<350, >205→255` + 페더(`docs/avatar-art/seoyeon/publish.py` 참조).
### 4.3 Live2D식 아바타(파츠 분리 리깅)
아바타는 기본 **SVG 파라미터 리그** 또는 **래스터 파츠 분리 리깅**(`apps/web/src/components/avatar/RasterBust.tsx`)으로 렌더. 후자는 `persona.rasterArtSet` 지정 시 활성.
- 레이어(각각 독립 opacity/교체 → 표정 중에도 깜빡임·입술싱크가 따로 움직임):
`base(neutral 전신)` + `upperface-<표정>(눈썹+눈)` + `eyelid-closed(깜빡임, 표정 무관)` + `mouth-<표정>` + `mouth-open(립싱크)`.
- 파이프라인(재현 스크립트는 `docs/avatar-art/seoyeon/`):
1. `codex_imagegen.sh`로 base + 표정 변주(sad/tired/anxious/warm/startled/eyes-closed/speaking) 생성. 변주는 base를 `-i` 참조로, 동일 평면 배경.
2. BiRefNet 누끼 → `publish.py`(표준 캔버스 900×1125 정규화 + 알파 정제)로 `apps/web/public/avatar/<artSet>/` 게시.
3. `make-parts.py`로 특징 영역(upperface/eyelid/mouth) 크롭+페더 파츠를 `parts/` 생성(영역 상수 튜너 블럭).
- 연결: `persona.ts`의 `AvatarPersona.rasterArtSet` / `Session.tsx`의 `PERSONA_AVATAR_LOOKS[<code>].rasterArtSet` / `RasterBust.tsx`(28표정→클러스터 매핑 포함).
- dev 미리보기(인증 없음): `/dev/avatar-preview`(`AvatarPreview.tsx`). 스크린샷: `node apps/web/scripts/avatar-shot.mjs`(BASE_URL 환경변수로 포트 지정).
- 실제 Live2D Cubism(`.moc3`)은 편집기 저작이 필요해 자동화 불가 → 위 레이어 합성이 실용적 대안.
### 4.4 진행 중인 아바타 작업 핸드오프
서연(P1) 아바타 작업은 **별도 세션에서 진행**. 현재 상태·남은 작업(Image #2=짧은 보브 기준 재생성 등)은
[`docs/ops/handoff-avatar-seoyeon-2026-06-27.md`](./docs/ops/handoff-avatar-seoyeon-2026-06-27.md) 참조.
### 4.5 페르소나별 Live2D 파츠 생성 규칙
P4~P7 등 다른 페르소나 파츠를 imagegen으로 만들 때는 `docs/avatar-art/personas/README.md`를 먼저 읽고 따른다.
- **시트 금지**: 여러 파츠를 한 이미지에 모으지 않는다. 파츠 하나당 생성 파일 하나다.
- **입력 참조는 원본 파츠 crop**: 900×1125 전체 캔버스가 아니라 원본 `alphaBox` 기준 tight crop을 imagegen 참조로 준다.
- **최종 산출물은 900×1125 투명 PNG**: 생성 결과에서 실제 파츠만 crop/resize한 뒤 원본 `alphaBox` 위치에 강제 paste한다.
- **검증 필수**: 최종 파츠 bbox drift는 원본 `alphaBox` 대비 2px 이내여야 한다. 검증 없는 DONE 표기 금지.
- **시각 QA 필수**: `visual_qa.py`로 앱 합성 순서의 neutral/sad preview와 contact sheet를 만들고 직접 본다. 눈 위치, 외계인 같은 비대칭, 초록 chroma-key 헤이즈, 엣지 블리딩, 여성/남성 페르소나 불일치를 잡기 전에는 앱에 연결하지 않는다.
- **남성/엣지 보정**: P5/P7처럼 남성 짧은 머리 페르소나는 긴 머리 슬롯을 그대로 채우면 안 된다. `apply_visual_overrides.py`로 긴 뒷머리·긴 사이드 슬롯을 비우고 리본형 outfit을 제거하며, 저알파 chroma-key 초록 엣지도 정리한 뒤 다시 visual QA를 통과시킨다.
- **기본 러너**: `python -X utf8 docs/avatar-art/personas/run_part_imagegen.py ...`.