## LLM 공급자 어댑터 (B)
- sister-agent/src/llm/ 에 LlmAdapter 인터페이스 신설. 어댑터 5종:
openclaw (기존), openai, anthropic, ollama, mock
- 선택은 LLM_PROVIDER 환경변수로. 기본값 mock.
- 모델명은 LLM_MODEL_{MANAGER,PRINCIPAL,LEAD,JUNIOR} 로 외부화.
OpenClaw 내부 네이밍이 기본값이지만 env 로 얼마든지 갈아끼움.
- openai 어댑터는 OPENAI_BASE_URL 로 OpenRouter / Azure / 로컬 llama.cpp
서버까지 커버.
## In-process 단일 프로세스 모드 (C)
- sister-agent/src/core.ts 로 executeInvocation 을 library-export
- rails 에 InProcessTransport 추가. 동적 import 로 sister-agent core 를
로드해 같은 Node 프로세스에서 함수 호출로 실행.
- DirectRailsClient 로 HTTP 루프백 없이 DB 에 직접 쓰기 — 단일
프로세스에서도 observability 동일.
- RailsConfig 에 transport: "in-process" 추가.
- buildTransports 가 RAILS_TRANSPORT 와 per-stage override 를 지원하도록
확장. 레거시 RAILS_TRANSPORT_MODE + RAILS_AGENT_*_HOST 도 그대로 호환.
## Git push 외부화 + allowlist env 화
- spawn.ts 의 ENABLE_GIT_PUSH 를 "GITEA_TOKEN 있으면 auto on" 으로 변경.
기존 Dev 토폴로지는 sister LXC 들에 이미 토큰이 있어서 행동 변화 없음.
- rails.service.ts 의 파일 프록시 allowlist 를 GIT_RAW_ALLOWED_HOSTS
env 로 외부화. 기본값은 기존 Gitea 호스트 유지.
## Docker / 배포
- Dockerfile 추가. 단일 이미지로 rails + sister-agent 둘 다 빌드.
- docker-compose.yml (기본): mariadb + rails 한 컨테이너 = in-process.
docker compose up 한 줄로 로컬 E2E 가능.
- docker-compose.full.yml: rails + 4 개 독립 sister 컨테이너 = 분산.
## 설정 샘플 + 문서
- .env.example 완전 재작성: 필수/LLM/토폴로지/Gitea/Discord 5 섹션
- rails.config.local.yaml: in-process 샘플
- rails.config.distributed.yaml: http 분산 샘플
- docs/LOCAL-SETUP.md: 30분 퀵스타트 (Docker + 네이티브 두 경로)
- README 에 "5분 퀵스타트" + 토폴로지 표 + LLM 공급자 목록 추가
## 테스트
- tests/transport-build.test.ts (6 테스트): 기본값 / in-process /
per-stage override / http 엔드포인트 누락 / env 기반 wiring / 레거시
env 호환
- 전체 테스트 105 → 111 통과
185 lines
9.3 KiB
Markdown
185 lines
9.3 KiB
Markdown
# hanarang-rails
|
||
|
||
> **4 자매가 달릴 결정론적 레일** — HaNaRang Rails
|
||
>
|
||
> _사용자는 출발 버튼만 누른다. 나머지는 자매들이 자동으로 달린다._
|
||
|
||
`hanarang-rails` 는 4 개의 AI "자매" 에이전트 (하랑 / 나랑 / 다랑 / 이랑) 가 하나의 요청을 받아 **기획 → 구현 → 리뷰 → 배포**를 자동으로 완주하는 결정론적 파이프라인 오케스트레이터다. 전임자 `hanarang-harness` 가 권고 기반이라 자매들이 중간에 길을 잃던 문제를, XState 유한 상태 기계 (FSM) 와 Sprint Contract 로 물리적으로 강제한다.
|
||
|
||
- **처음 보는 사람을 위한 완전 가이드**: [`docs/GUIDE.md`](docs/GUIDE.md) / [`docs/GUIDE.pdf`](docs/GUIDE.pdf)
|
||
- **설계 문서**: [`.plans/design/`](.plans/design/)
|
||
- **스프린트 명세**: [`.plans/sprints/`](.plans/sprints/)
|
||
- **실패 감사 (F1–F6)**: [`.plans/failure-audit.md`](.plans/failure-audit.md)
|
||
|
||
---
|
||
|
||
## 한 문단 요약
|
||
|
||
사용자가 `"todo 앱 만들어 줘"` 한 줄을 던지면, rails 오케스트레이터가 **하랑이 (기획) → 나랑이 (구현) → 다랑이 (리뷰) → 이랑이 (배포)** 순서로 파이프라인을 돌린다. 각 자매는 내부에서 **부장/수석/선임/신입** 4 단계 계층으로 태스크를 쪼개서 병렬 실행하고, 만들어낸 코드 파일은 자동으로 Gitea 에 public repo 로 push 되어 즉시 접근 가능한 URL 로 바뀐다. 대시보드에서는 이 모든 과정이 실시간으로 트리 형태로 보인다.
|
||
|
||
## 왜 다시 만들었는가
|
||
|
||
[`hanarang-harness`](https://git.nabomhalang.co.kr/hanarang/openclaw-harness) 에서 4 개월 운영하며 발견한 6 가지 고질 실패 모드:
|
||
|
||
| 코드 | 증상 | 원인 |
|
||
|---|---|---|
|
||
| F1 | 하네스 skill bypass — 자매가 혼자 worker 스폰 | skill 진입 강제 없음 |
|
||
| F2 | DoD 자동 강제 실패 — `build` 통과 = 완료로 판정 | sprint contract / validator 없음 |
|
||
| F3 | QA 단계 누락 — 사용자가 수동으로 다랑이 호출 | 자동 라우팅 없음 |
|
||
| F4 | 핸드오프 멘션 불안정 — 잘못된 자매 호출 | 분기가 LLM 판단에 의존 |
|
||
| F5 | 중간 끊김 — `request-timed-out` 반복 | 재시도/에스컬레이션 정책 없음 |
|
||
| F6 | 환경 검증 누락 — "Docker 없음" 으로 skip 허용 | 환경 전제 검사 없음 |
|
||
|
||
본질 한 줄: **"자매가 하네스를 안 타고 본인이 처리한다."**
|
||
|
||
## 6 가지 설계 원칙 (하드 룰)
|
||
|
||
1. **강제 > 권고** — 모든 파이프라인 전이는 코드로 강제한다.
|
||
2. **결정론적 FSM** — 자매 간 핸드오프는 XState 상태 전이다.
|
||
3. **Sprint Contract = 불변 계약** — DoD 를 Zod 스키마로 정의, validator 가 pass/fail 판정.
|
||
4. **Skill 강제 진입** — skill bypass 를 hook 이 감지해 차단.
|
||
5. **QA 체크리스트 의무** — 다랑이가 체크박스 전부 채워야 PASS.
|
||
6. **환경 검증 선행** — 실기동 검증 환경 없으면 스프린트 시작 자체를 거부.
|
||
|
||
## 아키텍처 개요
|
||
|
||
```
|
||
사용자 (Discord / Dashboard Web)
|
||
│
|
||
▼
|
||
┌─────────────────────────────┐
|
||
│ hanarang-dashboard │ Next.js 16 + NestJS
|
||
│ /rails, /office, /sisters │
|
||
└────────────┬────────────────┘
|
||
│ HTTP
|
||
▼
|
||
┌─────────────────────────────┐
|
||
│ hanarang-rails orchestrator │ XState + Prisma + MariaDB
|
||
│ FSM ─ Contract ─ Hierarchy │
|
||
└────────────┬────────────────┘
|
||
│ HTTP invoke
|
||
┌──────────┼──────────┬──────────┐
|
||
▼ ▼ ▼ ▼
|
||
┌─────┐ ┌─────┐ ┌─────┐ ┌─────┐
|
||
│하랑 │ │나랑 │ │다랑 │ │이랑 │ sister-agent × 4 LXC
|
||
│plan │ │impl │ │review│ │deploy│
|
||
└──┬──┘ └──┬──┘ └──┬──┘ └──┬──┘
|
||
└─────────┴─ openclaw CLI ────┘ (LLM: gpt-5.4 등)
|
||
│
|
||
▼
|
||
┌───────────────┐
|
||
│ Gitea SSOT │ git.nabomhalang.co.kr
|
||
│ (auto-push) │ public repo per pipeline
|
||
└───────────────┘
|
||
```
|
||
|
||
## 기술 스택
|
||
|
||
| 레이어 | 선택 |
|
||
|---|---|
|
||
| 런타임 | Node 22 + TypeScript strict |
|
||
| 상태 머신 | XState v5 |
|
||
| 스키마 | Zod |
|
||
| DB | MariaDB (Prisma) |
|
||
| 프로세스 | execa + AbortController |
|
||
| CLI | citty |
|
||
| 로그 | pino |
|
||
| 테스트 | Vitest |
|
||
| 프론트엔드 (대시보드) | Next.js 16 + styled-components |
|
||
| 백엔드 (대시보드) | NestJS + Socket.IO |
|
||
|
||
## 상태
|
||
|
||
- **v0.1.0** — Sprint 000~007 완료. FSM / Contract / QA / Migration 코어. 105 테스트 통과.
|
||
- **v0.1.1** — 실 LLM 통합 (OpenClaw infer), 4 계층 재귀 스폰, 파일 추출, Gitea auto-push, deploy URL.
|
||
- **v0.1.2** — 대시보드 아티팩트 뷰, FileViewerModal (MD 파일 클릭 → 모달).
|
||
- **v0.1.3** — LLM 제공자 어댑터 (OpenAI / Anthropic / Ollama / OpenClaw / mock), in-process 단일 프로세스 모드, docker-compose, Gitea 호스트 완전 외부화, 외부 배포 친화 .env.example.
|
||
|
||
## 빠른 시작 (Docker, 5 분)
|
||
|
||
가장 짧은 경로. 로컬에 `docker` 와 `docker compose` 만 있으면 된다.
|
||
|
||
```bash
|
||
git clone https://git.nabomhalang.co.kr/hanarang/hanarang-rails.git
|
||
cd hanarang-rails
|
||
cp .env.example .env
|
||
# .env 에서 LLM_PROVIDER=mock 으로 시작 (또는 openai/anthropic/ollama)
|
||
|
||
docker compose up --build
|
||
# → http://localhost:18800/health 확인
|
||
|
||
# 다른 터미널
|
||
curl -X POST http://localhost:18800/pipelines/start \
|
||
-H 'content-type: application/json' \
|
||
-d '{"project":"hello","requirements":"Say hi"}'
|
||
```
|
||
|
||
이게 끝. MariaDB + rails + sister-agent 4 개가 한 컨테이너 안에서 **in-process 모드** 로 돈다. 자세한 설정 옵션 (실제 LLM 키 연결, 네이티브 설치, 분산 토폴로지, 대시보드) 은 [`docs/LOCAL-SETUP.md`](docs/LOCAL-SETUP.md) 참조.
|
||
|
||
### 배포 토폴로지
|
||
|
||
| 모드 | 설명 | 파일 |
|
||
|---|---|---|
|
||
| **in-process** | 모든 것을 하나의 Node 프로세스에서. 로컬 개발 기본값 | `docker-compose.yml` · `rails.config.local.yaml` |
|
||
| **http 분산** | rails + 4 개 독립 sister-agent 컨테이너. 운영 토폴로지 | `docker-compose.full.yml` · `rails.config.distributed.yaml` |
|
||
| **mock** | FSM 만 검증 (LLM/파일/push 없음) | `RAILS_TRANSPORT=mock` 또는 `rails run --mock` |
|
||
|
||
### LLM 제공자
|
||
|
||
어댑터가 있어 다음 중 하나를 선택할 수 있다. `.env` 의 `LLM_PROVIDER` 로 지정:
|
||
|
||
- `mock` — API 키 없이 결정론 스켈레톤만 확인 (기본값)
|
||
- `openai` — OpenAI / OpenRouter / Azure OpenAI / OpenAI-호환 로컬 서버
|
||
- `anthropic` — Anthropic Messages API
|
||
- `ollama` — 로컬 Ollama 서버
|
||
- `openclaw` — hanarang 내부 전용 런타임
|
||
|
||
## CLI 서브커맨드
|
||
|
||
| 명령 | 용도 |
|
||
|---|---|
|
||
| `rails start` | 파이프라인 생성 |
|
||
| `rails run [--mock]` | E2E 실행 |
|
||
| `rails status [id]` | 상태 조회 + 타임라인 |
|
||
| `rails resume <id>` | escalated → idle 재개 |
|
||
| `rails abort <id>` | 강제 종료 |
|
||
| `rails contract generate/freeze/validate/show` | Sprint Contract 관리 |
|
||
| `rails qa run/show/templates` | QA 템플릿 실행 |
|
||
| `rails skill-context create/show/clear` | Skill 강제 진입 |
|
||
| `rails skill-trace show/blocked` | 도구 사용 감사 로그 |
|
||
| `rails doctor` | 환경 헬스체크 |
|
||
| `rails scaffold` | 신규 프로젝트 `.plans/` 생성 |
|
||
| `rails migrate from-hanarang-harness <path>` | 레거시 하네스 스캔 |
|
||
| `rails serve` | 오케스트레이터 HTTP 서버 |
|
||
|
||
## HTTP API (orchestrator, 18800)
|
||
|
||
| 메서드 | 경로 | 설명 |
|
||
|---|---|---|
|
||
| GET | `/health` | 헬스체크 |
|
||
| GET | `/pipelines?limit=N` | 파이프라인 리스트 |
|
||
| GET | `/pipelines/:id` | 파이프라인 상세 |
|
||
| POST | `/pipelines/start` | 새 파이프라인 실행 |
|
||
| POST | `/pipelines/:id/abort` | 강제 종료 |
|
||
| GET | `/api/pipelines/:id/sub-tasks` | SubTask 트리 |
|
||
| GET | `/api/sub-tasks/:id` | 서브태스크 상세 |
|
||
| GET | `/api/transitions` | 상태 전이 이력 |
|
||
| GET | `/api/escalations` | 에스컬레이션 큐 |
|
||
|
||
## 문서
|
||
|
||
- [`docs/LOCAL-SETUP.md`](docs/LOCAL-SETUP.md) — **30 분 퀵스타트** (본인 환경에서 처음 돌려 보기)
|
||
- [`docs/GUIDE.md`](docs/GUIDE.md) — **완전 가이드** (전 구간 해설, 처음 보는 사람용)
|
||
- [`docs/GUIDE.pdf`](docs/GUIDE.pdf) — 위 문서의 PDF 버전
|
||
- [`docs/migration-guide.md`](docs/migration-guide.md) — 레거시 → rails 이전 가이드
|
||
- [`docs/operations.md`](docs/operations.md) — 운영 가이드 (PM2, 로그, DB)
|
||
- [`docs/discord-setup.md`](docs/discord-setup.md) — Discord 봇 연동 + marker 프로토콜
|
||
- [`.plans/OVERVIEW.md`](.plans/OVERVIEW.md) — 프로젝트 전체 개요
|
||
- [`.plans/failure-audit.md`](.plans/failure-audit.md) — F1–F6 실패 감사
|
||
- [`.plans/design/`](.plans/design/) — 설계 문서 9 종
|
||
- [`.plans/sprints/`](.plans/sprints/) — 스프린트 상세
|
||
|
||
## 라이선스
|
||
|
||
MIT — 나봄하랑 / hanarang
|