docs(design): transport 추상화 + 트리거 3종 + opt-in 원칙 확정
- 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>
This commit is contained in:
@@ -18,23 +18,36 @@
|
||||
|
||||
## 범위 (Scope)
|
||||
|
||||
### 핵심 원칙: Rails 는 **opt-in**
|
||||
|
||||
Rails 는 4자매의 일상 대화를 가로채지 않는다. **명시적 트리거** 에서만 활성화된다. 트리거는 3종류:
|
||||
1. Slash command (`/rails start ...`)
|
||||
2. 하랑이 제안 후 사용자 승인
|
||||
3. Gitea webhook (자동 배포)
|
||||
|
||||
상세: [`design/triggers.md`](./design/triggers.md)
|
||||
|
||||
### In Scope
|
||||
- 4자매 역할: 하랑(Planner) / 나랑(Generator) / 다랑(Evaluator/QA) / 이랑(Infra/Deploy)
|
||||
- XState 기반 결정론적 오케스트레이터
|
||||
- Sprint Contract (Zod schema + validator)
|
||||
- Skill 강제 진입 (hook 기반 bypass 차단)
|
||||
- Skill 강제 진입 (hook 기반 bypass 차단, project 모드 한정)
|
||||
- 상태 전이 기반 자매 핸드오프
|
||||
- SisterTransport 추상화 (DiscordTransport v1, GatewayHttpTransport 후속)
|
||||
- 재시도 / 타임아웃 / 에스컬레이션
|
||||
- QA 체크리스트 runtime
|
||||
- 디스코드 알림 (사용자 알림 전용)
|
||||
- 디스코드 이중 채널: 자매 자연어 대화 (가시성) + rails payload 마커 (제어) + rails 상태 마커 (조감도)
|
||||
- Gitea webhook receiver (자동 배포 진입)
|
||||
- SQLite 기반 상태 영속화
|
||||
- 기존 hanarang-harness 자산 마이그레이션 (scaffold, install, agents md)
|
||||
|
||||
### Out of Scope
|
||||
- **4자매 일상 대화** — 기존 방식 그대로 유지. Rails 는 관여하지 않음
|
||||
- OpenClaw 런타임 자체의 수정
|
||||
- 자매 모델 자체 학습 / 파인튜닝
|
||||
- 디스코드 봇 기능 확장 (파이프라인 외 기능)
|
||||
- GitHub 연동 (Gitea 전용)
|
||||
- 멘션 기반 자매 간 핸드오프 (구 하네스 경로. 제거 대상 — SPRINT-007)
|
||||
|
||||
## 성공 기준 (Definition of Done)
|
||||
|
||||
@@ -108,7 +121,14 @@
|
||||
## 관련 문서
|
||||
|
||||
- [`failure-audit.md`](./failure-audit.md) — F1–F6 실패 감사
|
||||
- [`design/`](./design/) — 설계 문서 세트
|
||||
- [`design/state-machine.md`](./design/state-machine.md) — XState FSM 설계
|
||||
- [`design/sprint-contract.md`](./design/sprint-contract.md) — Sprint Contract + Zod validator
|
||||
- [`design/skill-enforcement.md`](./design/skill-enforcement.md) — 4계층 bypass 차단
|
||||
- [`design/handoff.md`](./design/handoff.md) — 자매 간 상태 전이 핸드오프
|
||||
- [`design/retry-policy.md`](./design/retry-policy.md) — 재시도 / 에스컬레이션
|
||||
- [`design/qa-template.md`](./design/qa-template.md) — QA 체크리스트 runtime
|
||||
- [`design/transports.md`](./design/transports.md) — SisterTransport 추상화 (Discord / HTTP / Mock)
|
||||
- [`design/triggers.md`](./design/triggers.md) — opt-in 트리거 3종 (slash / 하랑 제안 / gitea webhook)
|
||||
- [`sprints/`](./sprints/) — 스프린트 명세
|
||||
- [`migration/from-hanarang-harness.md`](./migration/from-hanarang-harness.md) — 마이그레이션
|
||||
|
||||
|
||||
214
.plans/design/transports.md
Normal file
214
.plans/design/transports.md
Normal file
@@ -0,0 +1,214 @@
|
||||
# Design — Sister Transport 추상화
|
||||
|
||||
> **처방 대상**: F4 (핸드오프 불안정) — 결정성 확보
|
||||
> **관찰 요구사항**: 자매 간 대화가 사용자에게 보여야 함 (자기야 피드백)
|
||||
> **현실 제약**: 기존 OpenClaw gateway HTTP API 스펙이 문서화돼 있지 않음
|
||||
|
||||
## 문제
|
||||
|
||||
Rails orchestrator 는 4자매를 결정론적으로 호출해야 하지만:
|
||||
1. **기존 hanarang-harness 는 HTTP gateway 를 안 쓴다** — 전부 `openclaw message send --channel discord` 경유
|
||||
2. **OpenClaw gateway API 스펙이 공개 안 됨** — `/health`, `/api/channels` 만 확인됨. 나머지는 reverse-engineering 필요
|
||||
3. **자기야는 자매 간 대화를 시각적으로 보고 싶어함** — 완전 HTTP 로 숨기면 UX 손상
|
||||
|
||||
## 해결: SisterTransport 인터페이스
|
||||
|
||||
Rails FSM 과 "어떻게 자매를 깨우는가" 를 분리. 전송 구현을 교체 가능하게 설계.
|
||||
|
||||
```ts
|
||||
interface SisterTransport {
|
||||
// 자매에게 구조화 task 전송, 응답 대기 (timeout 포함)
|
||||
invoke(req: InvokeRequest): Promise<HandoffMessage>
|
||||
|
||||
// 자매의 연결 상태 체크
|
||||
health(sister: SisterName): Promise<HealthStatus>
|
||||
|
||||
// (옵션) 자매가 중간 진행상황을 stream 할 때 구독
|
||||
subscribe?(sister: SisterName, callback: (progress: Progress) => void): Unsubscribe
|
||||
}
|
||||
```
|
||||
|
||||
## 구현 1 — DiscordTransport (v1 기본)
|
||||
|
||||
디스코드 채널을 **메시지 큐** 로 사용. 자매 자연어 대화는 그대로 유지, rails 가 보내는 task 는 별도 마커 블록으로 감쌈.
|
||||
|
||||
### 전송 (rails → 자매)
|
||||
|
||||
Rails 가 자매 전용 채널(또는 공용 파이프라인 스레드)에 메시지 post:
|
||||
|
||||
```
|
||||
<!-- rails:invoke v1 -->
|
||||
```json
|
||||
{
|
||||
"pipelineId": "01HW0XYZ",
|
||||
"contractId": "01HW0ABC",
|
||||
"role": "generator",
|
||||
"sprintId": "SPRINT-001",
|
||||
"task": {
|
||||
"title": "scaffold: Next.js + styled-components",
|
||||
"workdir": "/home/narang/projects/arang",
|
||||
"dodSummary": "...",
|
||||
"environmentPrerequisites": ["node22", "pnpm"]
|
||||
},
|
||||
"replyTo": { "channelId": "1234", "messageId": "5678" },
|
||||
"timeoutMs": 30000,
|
||||
"structuredOutput": true
|
||||
}
|
||||
```
|
||||
```
|
||||
<!-- /rails:invoke -->
|
||||
|
||||
나랑아, 이 작업 맡아줘. contract 는 `.rails/contracts/01HW0ABC.json`.
|
||||
```
|
||||
|
||||
- **기계 부분 (HTML 주석 안)** — 자매 봇이 LLM 우회해서 직접 파싱
|
||||
- **자연어 부분 (주석 밖)** — 자기야가 보는 용 + 자매가 맥락 이해용 참조
|
||||
|
||||
디스코드는 HTML 주석을 렌더링하지 않으므로 **자기야는 자연어만 보임**.
|
||||
|
||||
### 수신 (자매 → rails)
|
||||
|
||||
자매가 task 완료 후 같은 채널(또는 replyTo 지정 채널)에 post:
|
||||
|
||||
```
|
||||
<!-- rails:result v1 -->
|
||||
```json
|
||||
{
|
||||
"pipelineId": "01HW0XYZ",
|
||||
"actor": "narang",
|
||||
"verdict": "IMPL_DONE",
|
||||
"payload": {
|
||||
"branch": "feature/sprint-001",
|
||||
"commits": ["8fc6032"],
|
||||
"workdir": "/home/narang/projects/arang",
|
||||
"selfTestReport": {
|
||||
"typecheck": "pass",
|
||||
"build": "pass",
|
||||
"runtimeSmoke": "pass"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
```
|
||||
<!-- /rails:result -->
|
||||
|
||||
구현 완료했어요! branch: `feature/sprint-001`, tests 전부 통과했어.
|
||||
다랑이한테 넘길 준비 끝났습니다 ❤️
|
||||
```
|
||||
|
||||
Rails 는 Discord 봇으로 들어가 있어 `MESSAGE_CREATE` 이벤트를 구독. 해당 pipelineId 의 result 가 들어오면 FSM 에 이벤트 전달.
|
||||
|
||||
### 자매 봇 변경사항 (최소 침습)
|
||||
|
||||
자매 OpenClaw 쪽에 **메시지 디스패처** 를 추가:
|
||||
|
||||
```pseudo
|
||||
onDiscordMessage(msg):
|
||||
if msg.content contains '<!-- rails:invoke v1 -->':
|
||||
payload = extractJsonBlock(msg.content, 'rails:invoke v1')
|
||||
if payload.structuredOutput == true:
|
||||
# LLM 우회 경로: 결정론 파이프라인 모드
|
||||
result = processRailsInvoke(payload)
|
||||
postRailsResult(payload.replyTo, result)
|
||||
return # LLM 한테 안 넘김
|
||||
# 그 외는 기존 자연어 대화 경로
|
||||
llmRespond(msg)
|
||||
```
|
||||
|
||||
- `rails:invoke` 마커가 없으면 **기존 자유 대화 그대로**
|
||||
- 마커가 있으면 structured task 로 처리 → 자매 봇이 자기 내부 task runner 호출 → 결과를 marker 로 리턴
|
||||
- 중간에 자연어 메시지 postprocess 는 옵션 (자매가 "시작했어요" 같이 맥락 메시지 추가 가능)
|
||||
|
||||
이 변경이 SPRINT-007 마이그레이션의 핵심.
|
||||
|
||||
### 타임아웃 / 에스컬레이션
|
||||
|
||||
- Rails 가 invoke 후 `timeoutMs` 내 result 이벤트 못 받으면 TIMEOUT
|
||||
- Discord API 전송 실패는 네트워크 에러로 분류 → retry policy
|
||||
- result 의 Zod 검증 실패는 ERROR → retry policy
|
||||
|
||||
## 구현 2 — GatewayHttpTransport (후속 / v2)
|
||||
|
||||
OpenClaw gateway HTTP API 스펙 파악 후 구현. 현재는 **placeholder**.
|
||||
|
||||
### 조사 필요 사항
|
||||
- [ ] 가용한 엔드포인트 전수 (웹 UI 소스 + network tab 캡쳐)
|
||||
- [ ] 인증 방식 (Bearer? API key?)
|
||||
- [ ] agent invoke API 존재 여부
|
||||
- [ ] streaming / long-poll 지원
|
||||
- [ ] rate limit
|
||||
|
||||
### 잠재 장점 (스펙 파악되면)
|
||||
- 디스코드 rate limit 무관
|
||||
- 지연 감소
|
||||
- 중계 hop 감소 (Discord → 자매봇 → task runner 경로 단축)
|
||||
- 완전 로컬 테스트 가능 (디스코드 없이도 시뮬레이션)
|
||||
|
||||
### 구현 조건
|
||||
- Gateway API 가 agent invoke 를 지원하거나
|
||||
- Rails 팀이 `/api/rails/invoke` 같은 커스텀 엔드포인트를 gateway plugin 으로 추가
|
||||
- 후자가 더 안정적 (우리 통제)
|
||||
|
||||
## 구현 3 — MockTransport (테스트용)
|
||||
|
||||
테스트 / 로컬 개발용. 자매 없이 rails 자체를 시험 가능.
|
||||
|
||||
```ts
|
||||
class MockTransport implements SisterTransport {
|
||||
async invoke(req: InvokeRequest): Promise<HandoffMessage> {
|
||||
// 사전 정의된 시나리오 응답
|
||||
return MOCK_SCENARIOS[req.role][req.sprintId] ?? defaultMock(req)
|
||||
}
|
||||
async health() { return { alive: true, latencyMs: 1 } }
|
||||
}
|
||||
```
|
||||
|
||||
- `rails run --mock <project>` 서브커맨드로 mock 모드 강제
|
||||
- Sprint 001 의 end-to-end 테스트는 mock transport 로 돌림
|
||||
|
||||
## 설정 (config)
|
||||
|
||||
`.rails/config.yaml`:
|
||||
|
||||
```yaml
|
||||
transport:
|
||||
default: discord
|
||||
discord:
|
||||
botToken: ${DISCORD_TOKEN}
|
||||
guildId: ${DISCORD_GUILD_ID}
|
||||
pipelineCategory: 1234567890
|
||||
sisterChannels:
|
||||
harang: 1111
|
||||
narang: 2222
|
||||
darang: 3333
|
||||
erang: 4444
|
||||
# 나중에
|
||||
# gateway:
|
||||
# url: https://gw.hanarang.local
|
||||
# token: ${GATEWAY_TOKEN}
|
||||
|
||||
timeouts:
|
||||
invokeMs: 30000
|
||||
discordApiMs: 10000
|
||||
```
|
||||
|
||||
환경 변수 `RAILS_TRANSPORT=mock` 으로 override 가능 (테스트용).
|
||||
|
||||
## 자매 대화 가시성 (요약)
|
||||
|
||||
| 요소 | 어디 | 누가 포스팅 | 자기야가 봄 |
|
||||
|---|---|---|---|
|
||||
| 자매 자연어 메시지 | Discord 파이프라인 스레드 | 자매 본인 봇 | ✅ |
|
||||
| Rails 전송 payload | 같은 메시지의 `<!-- -->` 주석 | Rails 봇 또는 invoker | ❌ (자동 숨김) |
|
||||
| 자매 result payload | 같은 메시지의 `<!-- -->` 주석 | 자매 본인 봇 | ❌ |
|
||||
| Rails 상태 마커 | Rails 봇 | Rails | ✅ (조감도) |
|
||||
| 에스컬레이션 | Rails 봇 + @멘션 | Rails | ✅ (긴급 알림) |
|
||||
| 디버깅 상세 | `rails timeline` / `rails inspect` CLI | — | 요청 시 |
|
||||
| SQLite SoT | 로컬 DB | Rails | 요청 시 (`rails inspect`) |
|
||||
|
||||
## 참고
|
||||
|
||||
- `principles.md` 원칙 2 (결정론적 FSM)
|
||||
- `failure-audit.md` F4
|
||||
- `handoff.md` — HandoffMessage 스키마
|
||||
- OpenClaw gateway port: 18789 (LAN bind), auth 필요 (스펙 미확인)
|
||||
220
.plans/design/triggers.md
Normal file
220
.plans/design/triggers.md
Normal file
@@ -0,0 +1,220 @@
|
||||
# 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
|
||||
Reference in New Issue
Block a user