이전 커밋 08ea92f 은 잘못된 접근이었음. OpenClaw 가 이미 Discord 게이트웨이를
띄우고 있고 자매들의 봇 identity 는 거기 하나로 통일되어야 함. rails 가
discord.js 로 별도 봇을 등록하면 두 봇이 같은 채널에 공존하는 기이한 구조가
된다.
올바른 경로는 OpenClaw skill 의 user-invocable frontmatter 로 슬래시 커맨드를
노출하는 것이고, 이건 별도 커밋으로 ~/.openclaw/skills/hanarang-rails/ 에
반영됨.
삭제:
- src/bridge/ 전체 (discord-client, discord-commands, discord-notifier, index)
- tests/discord-notifier.test.ts
- package.json 의 discord.js 의존성
- src/cli/serve.ts 의 bridge 부트스트랩
- .env.example 의 DISCORD_* 블록
보존:
- runner.ts 의 PipelineLifecycleEvent + onEvent 훅 — 유닛 테스트/대시보드
WebSocket 등에 재사용 가능
- POST /pipelines/start-async 엔드포인트 — skill 의 polling wrapper 가 이걸 씀
- runner opts.pipelineId 지원 — async start 가 의존함
- http.ts ServerOpts 의 onPipelineEvent/notifier 파라미터 — 추상화는 유지,
serve.ts 가 주입을 안 할 뿐
테스트: 118 → 111 (discord-notifier 7 개 삭제), 나머지 그대로 통과.
v0.1.4 — 옵션 3 (outbound + inbound). DISCORD_TOKEN 이 설정되지 않으면
bridge 는 no-op 이라 기존 배포는 영향 없음.
## Outbound (rails → Discord)
- runner.ts: PipelineLifecycleEvent emitter 추가
started / stage-done / stage-failed / completed / failed / escalated
- DiscordNotifier: 이벤트 → Discord 메시지 렌더링
thread mode (슬래시 커맨드 트리거) vs channel mode (CLI/HTTP 트리거)
- EscalationNotifier 인터페이스도 구현 — escalate.ts 에서 사용자에게 알림
- runPipeline opts 에 onEvent + notifier 주입
## Inbound (Discord → rails)
- /rails start project:<name> requirements:<text> — 파이프라인 기동
→ defer reply → POST /pipelines/start-async → thread 생성 → 실시간 업데이트
- /rails status <id> — 상태 조회 (ephemeral)
- /rails abort <id> — 강제 종료 (ephemeral)
## Async start 엔드포인트
- POST /pipelines/start-async: pipelineId 즉시 리턴 후 background 에서
runPipeline 실행. Discord 의 3초 ACK 타임아웃을 회피.
- runPipeline 에 opts.pipelineId 지원: async 엔드포인트가 미리 만든
row 위에 파이프라인을 그대로 얹을 수 있게.
## 부트스트랩
- rails serve 가 DISCORD_TOKEN/GUILD_ID/NOTIFY_CHANNEL_ID 세 개가 모두
있으면 DiscordBridge 를 자동 시작. 없으면 "skip" 로그 남기고 무시.
- discord.js ^14.26 의존성 추가.
## 문서/테스트
- .env.example: Discord 섹션 전면 재작성 (동작 설명 포함)
- tests/discord-notifier.test.ts (7 tests): fake DiscordClientWrapper 로
라우팅/렌더링/바인딩 해제 로직 검증
- 총 111 → 118 테스트 통과