vignette/AGENTS.md
Yun Chan a0311c5957
Some checks failed
API contract / OpenAPI type drift (push) Failing after 3m27s
회기 무발화 0턴 분리, 자기예측 락 불변식 및 TDD 회귀 검증 완료
2026-09-08 23:28:06 +09:00

354 lines
30 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`.
## 📚 문서 맵 — 작업 전 해당 가이드를 먼저 읽어라
| 목적 | 문서 |
|---|---|
| **⚖️ 오케스트레이션 바이블(절대 규칙)** | 이 파일 **B절** — 고급 모델은 설계·평가, 하위 모델은 조사·구현·검증 실행. 양식 [`docs/ops/agent-work-packet-template.md`](./docs/ops/agent-work-packet-template.md) |
| **문서 인덱스(먼저 여기서 시작)** | [`docs/README.md`](./docs/README.md) |
| **할 일(안된 것들 통합 TODO)** | [`docs/TODO.md`](./docs/TODO.md) |
| 저장소 개요·빠른 시작 | [`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) |
| 냉동 보관소(읽지 않는다) | [`docs/archive/`](./docs/archive/README.md) |
작업 결과로 동작/구조가 바뀌면 해당 가이드와 SSOT 대시보드를 함께 갱신한다.
> **`docs/archive/`는 평상시 작업에서 읽지 않는다.** 완료·검증된 작업의 변경 로그, 실행이 끝난 구현/리팩터
> 계획, 일회성 smoke·PoC·리서치 스냅샷, 완료된 재설계 프롬프트만 냉동 보관한 이력이다. 현재 상태의 근거로
> 삼지 마라. 열린 작업은 얇은 현행 백로그와 SSOT 대시보드가 소유한다. 완료된 작업 기록이 다시 쌓이면
> `docs/archive/`로 옮겨 활성 문서를 얇게 유지한다.
---
## ⚖️ B. AI 에이전트 오케스트레이션 바이블 (절대 규칙 · 모든 절보다 우선)
> 이 절은 이 저장소의 모든 지침 중 최우선이며 절대 규칙이다. 다른 절·스킬·편의·속도와 충돌하면 이 절이 이긴다.
> 글로벌 지침(`~/.codex/AGENTS.md`, `~/.claude/CLAUDE.md`)에도 같은 내용이 있으며, **프로젝트 안에서의 원본은 이 파일이다.**
> 한쪽을 고치면 다른 쪽도 같은 내용으로 고친다. 진입점 `CLAUDE.md`·`AGENT.md`와 워커 정의(`.claude/agents/`)도 함께 맞춘다.
> 한 줄 요약: **고급 모델은 생각하고, 설계하고, 배정하고, 평가한다. 손은 하위 모델이 움직인다. 단, 설계와 평가는 절대 넘기지 않는다.**
### B.0 적용 대상
- **오케스트레이터**: 사용자와 직접 대화하는 최상위 세션이 고급 모델일 때. 이 절 전체가 적용된다.
- Codex: Astra(`gpt-6-astra`), Sol(`gpt-5.6-sol`)
- Claude Code: Fable 5.1(`claude-fable-5-1`), Opus 5(`claude-opus-5`)
- **워커**: 오케스트레이터가 하위 모델로 스폰한 에이전트(explorer / researcher / worker·implementer / verifier 등).
워커에게는 이 절의 "위임" 규칙이 적용되지 않는다. 워커는 받은 작업 패킷을 직접 수행하고, 설계를 바꾸지 않고,
다른 에이전트를 스폰하지 않고, 결과와 증거를 보고한다. 워커도 이 파일의 §0·§0.1·§2·§3은 그대로 따른다.
- 최상위 세션이 하위 모델(Sonnet, Haiku, Terra, Luna 등)이면 이 절의 위임 규칙은 적용하지 않고 직접 수행한다.
대신 설계 결정이 필요하면 고급 모델 세션으로 넘기라고 사용자에게 알린다.
### B.1 역할 원칙
- 고급 모델은 **메타인지와 오케스트레이션**에 집중한다: 문제 정의, 설계, 작업 분해, 작업 패킷 작성, 하위 모델 배정,
진행 감시, 결과 평가, 최종 결정, 사용자 보고.
- 매 작업마다 먼저 **경중을 따진다**: "이건 내가 직접 해야만 하는 일인가(B.2), 아니면 시킬 일인가(B.3)".
본인이 진짜 직접 해야 하는 일이 아니면 **직접 나서지 않고** 하위 모델에게 시킨다.
"내가 하는 게 빠르다", "컨텍스트에 이미 다 있다"는 직접 할 이유가 되지 못한다.
- 대규모 조사, 인터넷 웹서핑 조사, 논문·문서 스위핑, 코드베이스 탐색, 빌드·테스트 실행과 로그 수집처럼
**컨텍스트를 많이 먹거나 양이 많은 일**은 하위 모델 여러 개에 나눠 **멀티에이전틱(병렬)**으로 돌린다.
하나씩 순서대로 돌리지 않는다.
- 오케스트레이터의 컨텍스트는 설계와 판단에 쓰는 자원이다. 로그 원문, 검색 결과 원문, 대량 파일 내용으로 채우지 않는다.
### B.2 고급 모델이 절대 위임하지 않는 것 — 전면에서 직접 수행
- **중요한 결정 전부. 특히 설계.** 아키텍처, 모듈·계층 경계, 인터페이스/API/프로토콜 계약, 데이터 모델·스키마·
마이그레이션 방향, 상태 머신, 동시성·보안·성능·개인정보 전략, 기술·라이브러리 선택, 제품 방향에 영향을 주는
트레이드오프. 설계는 오케스트레이터가 직접 쓰고 직접 책임진다.
- 요구사항 해석, 범위 확정, 작업 분해, 작업 패킷 작성, 완료 기준(DoD) 정의.
- **개발 수행 결과 평가와 재개발 여부 판단.** 워커가 낸 diff·보고·검증 증거를 오케스트레이터가 직접 읽고 판단한다(B.6).
평가와 리뷰의 결론을 하위 모델에게 맡기지 않는다. 하위 모델은 증거를 모아 올 뿐이다.
- 최종 통합과 사용자 보고. 사용자에게 말하는 완료·수치·결론은 오케스트레이터가 직접 확인한 것만 쓴다.
- 파괴적이거나 되돌리기 어려운 작업: 원격 push, 배포, 릴리스, DB 마이그레이션 실행, 대량 삭제, 비밀정보·자격증명 취급.
위임하지 않고 직접, 필요하면 사용자 확인 후 수행한다. (이 저장소의 구체 목록은 B.10.)
### B.3 하위 모델에게 시키는 것
- **조사**: 웹 검색, 공식 문서·upstream 소스·이슈·PR 확인, 논문 스위핑, 레퍼런스·대안 비교표. 주제·출처·기간별로
쪼개 여러 researcher를 동시에 띄운다. 결과는 출처(URL, 문서 버전·날짜, 커밋·파일 경로)를 붙인 구조화된 보고로 받는다.
- **탐색**: 코드베이스 탐색, 호출 경로·영향 범위 추적, 재현 절차 확인, 관련 파일 목록화.
- **개발**: 설계와 작업 패킷이 확정된 구현, 리팩터, 테스트 작성, 버그 수정, 보일러플레이트, 마이그레이션 코드 작성.
워커는 설계를 바꿀 수 없다. 설계 변경이 필요하면 멈추고 보고하게 한다.
- **검증 실행**: 빌드·테스트·린트·재현 명령 실행, 로그 원문·스크린샷 등 증거 수집.
- 순서는 항상 "하위 모델이 수집·수행 → 오케스트레이터가 판단·평가"다. 반대로 하지 않는다.
### B.4 직접 수행이 허용되는 예외 (좁게 해석)
- B.2 항목.
- 위임 오버헤드가 작업보다 큰 사소한 일: 파일 한두 개의 몇 줄 수정, 단일 명령 실행, 사실 확인 한 번.
- 워커가 같은 작업에서 2회 반려된 뒤에도 실패해, 오케스트레이터가 핵심 경로를 직접 구현해야만 할 때.
이때 왜 직접 하는지 한 줄로 남긴다.
- 사용자가 "직접 하라"고 명시한 경우.
- 위 경우가 아닌데 직접 하고 있다면 멈추고 위임으로 전환한다.
### B.5 위임 절차 — 작업 패킷 없이는 위임하지 않는다
위임할 때는 워커에게 다음을 담은 작업 패킷을 넘긴다. 양식은
[`docs/ops/agent-work-packet-template.md`](./docs/ops/agent-work-packet-template.md).
1. 목표와 비목표.
2. 오케스트레이터가 확정한 설계·계약·불변량. **바꾸면 안 되는 것**을 명시한다.
3. 건드릴 파일·모듈 경계와 건드리면 안 되는 영역.
4. 완료 기준과 검증 명령(테스트·빌드·린트·재현). 검증 명령은 B.10의 표준 세트에서 고른다.
5. 보고 형식: 변경 파일, diff 요약, 실행한 명령과 결과 원문(통과/실패 수), 미해결·의문점, 설계 변경 요청 여부.
6. 제약: 다른 에이전트 스폰 금지, fallback·silent catch·mock·테스트 스킵으로 통과 위장 금지, 임시 주석·디버그 코드 금지,
커밋·push는 허용된 경우만. 병렬 워커가 있으면 "다른 워커의 변경을 되돌리지 말 것"과 각자의 경계를 명시한다.
작업 패킷을 쓸 수 없을 만큼 설계가 불확실하면 그것은 위임할 때가 아니라 오케스트레이터가 설계를 먼저 끝내야 한다는 신호다.
### B.6 결과 평가 게이트 — 시켜 놓고 방치하지 않는다
- 워커가 끝나면 오케스트레이터가 **즉시** 평가한다. 다른 워커를 띄우고 잊거나, 워커의 보고를 사용자에게 그대로 전달하지 않는다.
- 평가 항목:
- (a) 작업 패킷·확정 설계 준수 여부, 범위 이탈 여부.
- (b) 정확성과 회귀 위험 — **diff를 직접 읽는다.**
- (c) 검증 증거의 진위 — 테스트·빌드가 실제로 통과했는지, 스킵·우회·완화·fallback·삼킨 오류가 없는지.
의심되면 verifier를 띄워 재실행 원문을 받는다.
- (d) 노이즈 — 임시 주석, 디버그 출력, TODO, 무관한 변경.
- (e) 워커가 올린 의문점·설계 변경 요청에 대한 답.
- 판정은 셋 중 하나로 명시한다: **수용** / **반려 후 재작업**(반려 사유와 수정 지시를 패킷에 추가) /
**폐기 후 재배정 또는 직접 수행**.
- 워커의 "완료했습니다"는 증거가 아니다. 증거는 명령과 결과 원문이다. 확인하지 않은 결과를 사용자에게 완료라고 보고하지 않는다.
- 같은 워커가 같은 이유로 2회 실패하면 워커 탓보다 패킷(설계·지시·완료 기준)을 먼저 의심하고 오케스트레이터가 설계를 재점검한다.
- 사용자에게 보고할 때 무엇을 시켰고 무엇을 수용·반려했는지 짧게 남긴다.
### B.7 금지
- 설계를 하위 모델에게 맡기고 결과만 받아 쓰는 것.
- 평가·리뷰를 하위 모델에게 위임하고 그 결론을 그대로 채택하는 것.
- 조사 결과를 출처 확인 없이 사실로 채택하는 것.
- 워커가 설계를 변경하거나 다른 워커를 스폰하는 것.
- 위임이 귀찮다는 이유로 고급 모델이 대규모 조사·단순 구현·로그 읽기를 직접 다 하는 것.
- 오케스트레이터 모델(Astra/Sol/Fable/Opus)을 워커 모델로 스폰하는 것(사용자 명시 요청 제외).
### B.8 Codex 실행 규격 (모델 계층과 도구)
- 오케스트레이터: Astra(`gpt-6-astra`) 또는 Sol(`gpt-5.6-sol`). 설계·평가 단계에서는 reasoning effort를 `high` 이상으로 쓴다.
Sol/Terra의 `ultra`는 "자동 작업 위임"이 붙은 최대 추론 단계다.
- 워커 역할은 `~/.codex/agents/*.toml`(글로벌)에 정의돼 있다. 스폰할 때 역할명을 지정한다. 참조 사본과 설치법은
[`docs/ops/codex-agents/`](./docs/ops/codex-agents/README.md). 프로젝트 스코프 `.codex/agents/`는 스폰 실패 이슈
(openai/codex#26408, 2026-09-06 기준 open)가 닫힐 때까지 쓰지 않는다. 프로젝트 `.codex/config.toml`도 두지 않는다.
| 역할 | 모델 | 샌드박스 | 용도 |
|---|---|---|---|
| `explorer` | `gpt-5.6-luna` | read-only | 코드베이스 탐색, 호출 경로·영향 범위, 근거(파일:줄) 수집 |
| `researcher` | `gpt-5.6-luna` | read-only | 웹·공식 문서·upstream·이슈/PR·논문 스위핑, 출처 포함 보고 |
| `worker` | `gpt-5.6-terra` | 상위 상속 | 작업 패킷 기반 구현·리팩터·테스트 작성 |
| `verifier` | `gpt-5.6-luna` | 상위 상속 | 빌드·테스트·린트·재현 실행, 결과 원문 보고(판정 없음) |
- 역할을 지정하지 않고 스폰하면 `~/.codex/config.toml``[agents]`(enabled=true, `default_subagent_model = "gpt-5.6-terra"`,
`default_subagent_reasoning_effort = "medium"`, `max_concurrent_threads_per_session = 8`)가 적용된다. 워커에 Astra/Sol을 쓰지 않는다.
- 흐름: 작업 패킷 작성 → `spawn_agent`로 병렬 스폰(조사는 주제별로 researcher 여러 개) → `wait_agent`(타임아웃은 작업 규모에 맞게)
→ B.6 평가 → 반려 시 `send_input`으로 재작업 지시 → 끝나면 `close_agent`.
- 스폰 메시지에 항상 포함: "너는 워커다. 다른 에이전트를 스폰하지 마라. 설계를 바꾸지 마라. 병렬 워커의 변경을 되돌리지 마라.
이 저장소의 AGENTS.md §0·§2·§3을 따르라."
- 여러 워커가 같은 저장소를 만지면 파일·모듈 경계를 겹치지 않게 나누고, 겹치면 순차로 돌린다.
- Orca 하네스 안에서는 `orchestration` 스킬(`orca orchestration task-create` / `dispatch`)로 다른 터미널의 Claude Code·Codex
워커에게 디스패치할 수 있다. 이때도 워커 모델은 하위 티어로 지정하고, 작업 패킷과 B.6 평가 게이트를 똑같이 적용한다.
디스패치한 뒤 `worker_done`을 기다려 결과를 직접 평가한다.
### B.9 Claude Code 실행 규격 (모델 계층과 도구)
- 오케스트레이터: Fable 5.1(`claude-fable-5-1`) 또는 Opus 5(`claude-opus-5`).
- 워커 계층: Sonnet 5(`sonnet`) = 구현·검증 실행, Haiku 4.5(`haiku`) = 대량 조사·스위핑·탐색.
- 워커 정의는 이 저장소의 [`.claude/agents/`](./.claude/agents/)(프로젝트, 커밋됨)에 있고, `~/.claude/agents/`(글로벌)와
같은 내용을 유지한다. 프로젝트 정의가 우선한다.
| 서브에이전트 | 모델 | 도구 제한 | 용도 |
|---|---|---|---|
| `researcher` | Haiku 4.5 | 읽기·웹만, 쓰기·Agent 금지 | 웹·공식 문서·upstream·이슈/PR·논문 스위핑, 출처 포함 보고. 주제별로 여러 개 병렬 |
| `implementer` | Sonnet 5 | Agent 금지 | 작업 패킷 기반 구현·리팩터·테스트 작성 |
| `verifier` | Sonnet 5 (low) | Bash·읽기만, 쓰기·Agent 금지 | 빌드·테스트·린트·재현 실행, 결과 원문 보고(판정 없음) |
| 내장 `Explore` | 기본 서브에이전트 모델 | 읽기 전용 | 코드베이스 탐색·파일 위치 찾기 |
- 모델을 지정하지 않은 서브에이전트(내장 `Explore` 등)는 이 저장소의 [`.claude/settings.json`](./.claude/settings.json)
`subagentModel = "sonnet"`에 따라 Sonnet으로 돈다(우선순위: frontmatter `model` > 환경변수 `CLAUDE_CODE_SUBAGENT_MODEL` > `subagentModel`).
`subagent_type: "fork"`는 부모(고급) 모델을 상속하므로 대량 작업·조사에 쓰지 않는다.
- 흐름: 작업 패킷 작성 → `Agent` 도구로 스폰(독립 작업은 **한 메시지에 여러 Agent 호출**로 병렬) → 완료 알림 수신
→ B.6 평가 → 반려 시 `SendMessage`로 같은 에이전트에 재작업 지시 → 수용.
- 내장 `Plan` 에이전트는 설계 대행이 아니다. 설계 결정은 오케스트레이터가 직접 한다. `Plan`은 자료 정리·후보 열거에만 쓴다.
- 외부 레인: `codex exec --full-auto`를 구현 워커로 쓸 수 있다. 이 경우에도 작업 패킷과 B.6 평가 게이트를 똑같이 적용한다.
- 사용자가 ultracode를 켰거나 워크플로를 요청한 세션에서는 `Workflow`로 대규모 팬아웃(조사·검증)을 한다.
그 외에는 `Agent` 병렬 호출이 기본이다.
- Orca 하네스 디스패치 규칙은 B.8과 같다.
### B.10 Vignette 바인딩 — 이 저장소에서의 구체 적용
- **워커도 이 파일을 따른다**: §0(OS 선파악)·§0.1(PowerShell 5.1)·§2(검증 기준)·§3(한글·커밋 문구). 패킷에 명시한다.
- **표준 검증 명령 세트** (패킷의 검증 명령은 여기서 고른다. 상세는 [`docs/guides/testing.md`](./docs/guides/testing.md)):
| 대상 | 작업 디렉터리 | 명령 | 외부 의존 |
|---|---|---|---|
| 백엔드 단위 | `apps/api` | `python -m pytest app/ -q` | 없음 |
| 엔진 게이트웨이 | `apps/api` | `python -m pytest engine_gateway/ -q` | 없음 |
| API 타입 동기화 | `apps/web` | `npm run check:api-types` | 없음 |
| 웹 타입체크·빌드 | `apps/web` | `npm run typecheck` · `npm run build` | 없음 |
| 레이아웃 E2E 게이트 | `apps/web` | §2의 `layout-visual-gate`(7/7)·`session-layout`(8/8) | web+api+DB 스택(`scripts\dev-up.ps1`) |
- **SSOT 반영은 오케스트레이터 판정 뒤에만**: 워커 보고는 `docs/dev_dashboard.html`·`docs/TODO.md`·핸드오프 문서의 상태 변경 근거가
아니다. B.6 판정(수용)을 거친 결과만 반영한다. 대시보드 편집 작업 자체는 워커에게 시켜도 되지만 "무엇을 DONE으로 쓸지"는
오케스트레이터가 정하고, 편집 뒤 `python -X utf8 scripts/check-dev-dashboard-ssot.py`를 통과시킨다.
- **이 저장소의 파괴적·직접 수행 목록**(B.2): Forgejo/NAS 정식 배포와 compose 교체, DB 마이그레이션 적용, `scripts/backup-vignette-db.ps1`
복원 경로, `data/`·`uploads/`·운영 볼륨 삭제, `.env*`·OAuth secret·NAS 자격증명 취급, 원격 push. 오케스트레이터가 직접, 소유자 확인 후.
- **병렬 경계 기본값**: `apps/api``apps/web``docs`/`scripts`를 워커별로 나눈다. 같은 파일을 두 워커가 만지지 않는다.
`apps/web/src/lib/api.gen.ts`는 생성물이므로 손으로 고치지 않고 `check:api-types`로 맞춘다.
- **평가 기록**: 판정(수용/반려/폐기)과 근거는 사용자 보고에 남긴다. 세션을 넘기는 장기 작업은 핸드오프 문서에 패킷 제목·판정·미해결을 요약한다.
---
## ⚠️ 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. 운영 원칙
- **개발 체계는 B절(오케스트레이션 바이블)을 따른다.** 고급 모델 오케스트레이터가 설계·작업 패킷·평가·보고를 직접 하고,
조사·탐색·구현·검증 실행은 하위 모델 워커에게 시킨다. 워커 보고는 증거가 아니다.
- **에이전트 지침의 원본은 이 파일(`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 ...`.