- design/transports.md — DiscordTransport (v1) / GatewayHttpTransport (v2) / MockTransport Rails invoke payload를 HTML 주석으로 감싸 디스코드 전송 (자기야는 자연어만 봄) - design/triggers.md — opt-in 3종: 1. /rails start 슬래시 커맨드 2. 하랑이 제안 → 사용자 승인 3. Gitea webhook → 자동 배포 진입 - OVERVIEW.md — scope에 opt-in 원칙 + 일상대화 Out of Scope 명시 + 문서 링크 9개로 확장 결정: 자기야 승인 — 2026-04-10 확인 - DiscordTransport 를 v1 기본 transport 로 채택 - 자매 자연어 포스팅은 그대로 유지 (가시성) - gateway HTTP API 는 v2 에서 스펙 확인 후 전환 Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
221 lines
7.2 KiB
Markdown
221 lines
7.2 KiB
Markdown
# Design — Rails 트리거 모드
|
|
|
|
> **Rails 는 opt-in**. 4자매의 일상 대화를 가로채지 않는다.
|
|
> Rails 가 "켜지는" 경로는 명시적으로 3가지 뿐.
|
|
|
|
## 원칙
|
|
|
|
- **Casual 대화 vs Project 모드의 경계가 명확해야 한다.**
|
|
- Casual: 자매 자유 대화. Rails 개입 0. SQLite 기록 없음.
|
|
- Project: Rails FSM 이 주도. sprint contract + enforcement 적용.
|
|
- **자동 가로채기 금지.** 사용자 의도 없이 project 모드로 전이할 수 없다.
|
|
- **하랑이는 제안만 할 수 있다.** 실제 시작은 사용자 승인 필요.
|
|
|
|
## 트리거 모드 3종
|
|
|
|
### 트리거 1 — Slash Command (가장 명시적)
|
|
|
|
Rails Discord 봇이 등록한 슬래시 커맨드:
|
|
|
|
```
|
|
/rails start <project> "<요구사항>"
|
|
/rails start arang "Sprint 002 — Live2D 아바타 추가"
|
|
/rails status [pipeline-id]
|
|
/rails resume <pipeline-id>
|
|
/rails abort <pipeline-id>
|
|
/rails inspect <pipeline-id>
|
|
/rails timeline <pipeline-id>
|
|
```
|
|
|
|
**흐름**:
|
|
1. 사용자가 디스코드에서 `/rails start arang "..."` 실행
|
|
2. Rails 봇이 파이프라인 ID 생성 + Discord 포럼 스레드 생성
|
|
3. 하랑이 호출 (DiscordTransport invoke)
|
|
4. FSM 이 `idle → planning` 전이
|
|
5. 이후 파이프라인 끝까지 자동 진행
|
|
|
|
**장점**:
|
|
- 가장 명확. opt-in 이 100% 확실
|
|
- Rails 봇이 권한 체크 가능 (자기야만 실행 허용 등)
|
|
- 파라미터 검증 즉시
|
|
|
|
### 트리거 2 — 하랑이의 제안 (자연스러움)
|
|
|
|
평소 채팅 중 프로젝트 스케일 요청이 감지되면 **하랑이가 제안만** 함. 자동 실행 금지.
|
|
|
|
```
|
|
자기야: "아랑이 proactive 엔진 진짜 필요할 것 같아. 한 번 만들어볼래?"
|
|
|
|
하랑이 🦊: 이거 Sprint 단위 작업 같은데, rails 파이프라인으로 넘길까?
|
|
요구사항: "Arang Proactive 엔진 프로토타입"
|
|
예상 타입: feature
|
|
예상 스프린트: 2~3개 분해 예정
|
|
|
|
승인하면 다음 커맨드를 실행해줘:
|
|
/rails start arang "Proactive 엔진 프로토타입"
|
|
|
|
(또는 "ㅇㅇ" 이라고 답하면 내가 propose 파일 만들어두고
|
|
자기야가 확인만 하면 실행되도록 할게)
|
|
```
|
|
|
|
**흐름**:
|
|
1. 하랑이 시스템 프롬프트에 "project-scale 감지 시 rails 제안" 룰 내장
|
|
2. 감지 조건 (예시):
|
|
- "프로젝트", "기획", "구현", "배포", "Sprint", "만들어줘" 등 키워드
|
|
- 다단계 작업 필요 판단
|
|
- 여러 자매 협업 필요 판단
|
|
3. 하랑이가 **제안 메시지** 포스팅 (실행 아님)
|
|
4. 사용자가:
|
|
- 옵션 A: 슬래시 커맨드 직접 실행
|
|
- 옵션 B: "ㅇㅇ" / "응" 같이 짧게 동의
|
|
5. 옵션 B 인 경우, 하랑이는 `.rails/proposals/` 에 proposal 파일 생성하고, rails 봇이 이를 감지해 `proposal` 상태로 등록
|
|
6. 사용자가 `/rails approve <proposal-id>` 또는 제안 메시지의 확인 버튼을 누르면 실제 `rails start` 실행
|
|
|
|
**자동 실행 안 하는 이유**:
|
|
- 하랑이 LLM 이 잘못 감지할 수 있음
|
|
- 사용자 의도 확인이 반드시 필요
|
|
- `principles.md` 원칙 4 (강제 > 권고)
|
|
|
|
**하랑이 제안 → 승인 트랜잭션**:
|
|
```
|
|
proposal 상태 (.rails/proposals/<id>.json):
|
|
{
|
|
"id": "01HW0PR0P",
|
|
"proposedBy": "harang",
|
|
"at": "2026-04-10T13:00:00Z",
|
|
"project": "arang",
|
|
"title": "Proactive 엔진 프로토타입",
|
|
"rationale": "다단계 작업 + proactive 감지 + 설정 UI 필요",
|
|
"status": "pending",
|
|
"expiresAt": "2026-04-10T14:00:00Z" // 1시간 후 자동 만료
|
|
}
|
|
|
|
사용자 승인:
|
|
→ status: "approved"
|
|
→ rails start 트리거
|
|
→ pipelineId 생성 후 status: "consumed"
|
|
```
|
|
|
|
### 트리거 3 — Gitea Webhook (자동 배포 진입)
|
|
|
|
특정 이벤트 발생 시 rails 가 **배포 단계부터** 자동 진입. 기획/구현은 안 함.
|
|
|
|
#### 지원 이벤트
|
|
|
|
| Gitea 이벤트 | Rails 액션 | 진입 상태 |
|
|
|---|---|---|
|
|
| `push` to main with tag (`v*`) | 이랑이 deploy actor 자동 호출 | `deploying` |
|
|
| `push` to main without tag | 이랑이 `smoke-check` actor | `verifying` |
|
|
| `pull_request` merged to main | 다랑이 post-merge review | `post-review` |
|
|
| `release created` | 이랑이 배포 수행 | `deploying` |
|
|
|
|
#### 흐름 (tag push 예시)
|
|
```
|
|
1. 개발자가 push tag v0.2.0 to hanarang/arang
|
|
2. Gitea webhook → POST https://rails.hanarang.local/webhook/gitea
|
|
3. Rails webhook receiver:
|
|
- Gitea signature 검증 (HMAC)
|
|
- 이벤트 타입 + 저장소 + ref 확인
|
|
4. Rails 가 새 파이프라인 생성 (`type: deploy-only`)
|
|
5. FSM 을 `idle → deploying` 상태로 직접 전이
|
|
6. 이랑이(erang) actor 호출 — project type 감지 → 배포 수행
|
|
7. Discord 파이프라인 스레드 생성 + 진행 알림
|
|
8. 배포 완료 후 `done` 전이
|
|
9. 사용자에게 배포 결과 알림 (디스코드 멘션)
|
|
```
|
|
|
|
#### 보안
|
|
- Webhook URL 은 Gitea 내부망에서만 접근 가능 (vmbr1)
|
|
- HMAC 서명 검증 필수
|
|
- IP 화이트리스트 (Gitea 서버 IP 만)
|
|
- secret 은 `.env` 에 `GITEA_WEBHOOK_SECRET`
|
|
- 실패 시 즉시 로그 + 알람
|
|
|
|
#### 저장소별 구성
|
|
`.rails/webhooks.yaml`:
|
|
```yaml
|
|
repositories:
|
|
- org: hanarang
|
|
repo: arang
|
|
events: ['push', 'tag', 'release']
|
|
branches: ['main']
|
|
pipelineType: deploy-only
|
|
deployTarget: production
|
|
- org: hanarang
|
|
repo: hanarang-rails
|
|
events: ['tag']
|
|
pipelineType: deploy-only
|
|
deployTarget: self-install
|
|
```
|
|
|
|
## 비트리거 (안 함)
|
|
|
|
Rails 는 다음 상황에서 **자동 개입하지 않는다**:
|
|
|
|
- 자매 간 일상 대화
|
|
- 자기야와의 일상 대화 (잡담, 질문, 상담)
|
|
- Discord 의 일반 메시지 (rails 마커 없는)
|
|
- 멘션 (`@나랑이` 등) — 기존 자매 봇 경로로만 작동
|
|
- 단순 파일 편집 (git commit 등)
|
|
- CI 실행 결과 (별도 CI 시스템 이미 있으면 그쪽 우선)
|
|
|
|
## 모드 구분 가드 (자매 봇 쪽)
|
|
|
|
자매가 메시지를 받으면:
|
|
|
|
```pseudo
|
|
onDiscordMessage(msg):
|
|
# Project 모드 (Rails invoke)
|
|
if msg.content has '<!-- rails:invoke v1 -->':
|
|
return handleRailsInvoke(msg)
|
|
|
|
# Casual 모드 (기존 대화)
|
|
llmRespond(msg)
|
|
```
|
|
|
|
- 마커 없으면 무조건 Casual. LLM 정상 응답.
|
|
- 마커 있으면 Project. structured 처리.
|
|
- 한 메시지 안에 두 모드 섞을 수 없음 (동시 발생 X).
|
|
|
|
## 제안 메시지의 최소 스펙 (하랑이 쪽)
|
|
|
|
하랑이가 Project 감지 시 생성하는 제안 메시지 템플릿:
|
|
|
|
```markdown
|
|
**📋 Rails 파이프라인 제안**
|
|
|
|
감지된 project-scale 작업:
|
|
- 프로젝트: `{project}`
|
|
- 제목: {title}
|
|
- 예상 타입: `{feature|bugfix|scaffold|infra|migration|refactor}`
|
|
- 근거: {rationale}
|
|
|
|
실행하려면:
|
|
```
|
|
/rails start {project} "{title}"
|
|
```
|
|
|
|
또는 "ㅇㅇ" / "응" 이라고 답하면 proposal 파일을 만들어둘게.
|
|
|
|
<!-- rails:proposal v1 -->
|
|
```json
|
|
{
|
|
"proposalId": "01HW0PR0P",
|
|
"project": "{project}",
|
|
"title": "{title}",
|
|
"type": "{type}",
|
|
"rationale": "{rationale}"
|
|
}
|
|
```
|
|
<!-- /rails:proposal -->
|
|
```
|
|
|
|
**주의**: 마커는 rails 봇이 감지. 자기야는 주석 없이 자연어 메시지만 봄.
|
|
|
|
## 관련 문서
|
|
|
|
- `transports.md` — DiscordTransport (구조화 payload 전달)
|
|
- `handoff.md` — HandoffMessage 스키마
|
|
- `skill-enforcement.md` — Rails 모드 진입 후 hook 동작
|
|
- `principles.md` — 원칙 1, 4
|