Codex와 OpenCode 세션 포크하기 (UI/TUI에서 또는 휴대폰에서)
적절한 지점에서 세션을 분기하는 실용 가이드(메시지에서 포크 vs 최신에서 포크)와 포크 후 맥락이 어떻게 되는지.
- CLI
- 0.2.17
- 프리뷰 ref
- a2c45c8
포크는 그냥 분기입니다.
원래 세션은 그대로 두고, 더 이전 지점에서 시작하는 자식 세션을 만듭니다.
이 가이드는 Codex와 OpenCode 세션을 포크하는 직관적인 방법입니다.
- 각자의 UI/TUI에서(지원되는 경우)
- 휴대폰/브라우저 UI에서(Happier가 한 가지 방법)
제대로 정해야 하는 건 어디서 포크하느냐뿐입니다.
요약
- 어디서 분기할지 정합니다:
- 최신에서 포크("지금"에서 분기)
- 메시지에서 포크(더 이전에서 분기)
- Codex에서는:
- 최신 세션 상태에서 포크하는 것이 안정적입니다
- OpenCode에서는:
- OpenCode 서버 백엔드를 쓰면 서버 네이티브 메시지에서 포크가 가능합니다
- 기기 간에 일관된 포크 UI를 원한다면 Happier가 한 가지 방법입니다. 트랜스크립트 안에 "Fork from this message"가 있고 자식 세션을 자동으로 만듭니다.
"포크"의 실제 의미
포크는 복사/붙여넣기가 아닙니다.
포크는:
- 새 세션 ID
- 조상 맥락이 연결됨
- 포크 지점부터 새 메시지 흐름
원래 세션은 바뀌지 않습니다.
최신에서 포크 vs 메시지에서 포크 (고르는 법)
최신에서 포크
이럴 때:
- 지금 방향이 마음에 든다
- 다른 접근을 병렬로 시도하고 싶을 뿐이다
메시지에서 포크
이럴 때:
- 갈라지고 싶은 정확한 순간을 짚을 수 있다
- 지시를 깔끔하게 다시 하고 싶다
방법 A: Codex 안에서 포크
Codex 지원은 화면마다 다르지만, 워크플로는 보통 다음과 비슷합니다.
- 현재 세션 포크(최신에서 포크)
- 포크한 세션을 새 분기로 계속
Codex에서 최신에서 포크가 맞는 경우
최신에서 포크가 좋은 경우:
- 다른 구현 경로를 시도하고 싶다
- 두 접근을 비교하고 싶다
- 위험한 변경 직전에 안전한 분기가 필요하다
실제로는 메시지에서 포크가 필요한 경우
메시지에서 포크는 "되감고 싶다"는 동작입니다.
- 지시를 깔끔하게 다시 하고 싶다
- 세션이 궤도를 벗어나기 전에 갈라지고 싶다
쓰는 Codex 화면이 메시지에서 포크를 지원하지 않으면 흔한 대안은 리플레이입니다.
- 새 세션 만들기
- 원래 세션처럼 동작할 만큼의 맥락 주기
(그래서 좋은 "포크 UX"에는 항상 리플레이 경로가 있습니다.)
방법 B: OpenCode 안에서 포크
OpenCode는 클라이언트/서버 구조가 탄탄합니다.
OpenCode 서버 백엔드를 쓰고 있다면 서버 네이티브 메시지에서 포크를 쓸 수 있습니다.
간단한 OpenCode 판단 규칙
- 분기할 정확한 메시지를 안다 → 메시지에서 포크
- 아니면 → 최신에서 포크
현재 OpenCode 화면에서 메시지에서 포크를 쓸 수 없다면 여기서도 리플레이 대안이 적용됩니다.
방법 C: 휴대폰에서 포크 (Happier)
휴대폰/브라우저/데스크톱에서 일관된 포크 UI를 원한다면 Happier가 한 가지 방법입니다.
구현에 근거한 모습은 다음과 같습니다.
특정 메시지에서 포크
아주 유용한 포크인데, 많은 도구가 의외로 어렵게 만들어 둔 기능입니다.
Happier에서는:
- 세션 트랜스크립트를 엽니다.
- 분기할 메시지를 찾습니다.
- Fork from this message를 선택합니다.
중요한 세부 사항 두 가지:
- 포크 버튼은 그 메시지를 포크 지점으로 쓸 수 있을 때만 나타납니다.
- Happier는 시퀀스 번호로 포크하므로(
forkPoint: { type: 'seq', upToSeqInclusive }) 포크 지점이 안정적입니다.
알아 둘 만한 작은 UX 디테일
커밋된 사용자 메시지에서 포크하면 Happier는 이를 "이 지시를 깔끔하게 다시 하기"로 다룹니다.
- 사용자 메시지 앞에서 포크한다
- 사용자 메시지 텍스트를 자식 컴포저에 편집 가능한 초안으로 복원한다
그래서 다음을 할 수 있습니다.
- 표현 다듬기
- 빠진 제약 하나 추가
- 깔끔하게 다시 보내기
전체 설정을 다시 쓸 필요가 없습니다.
(구현 근거: resolveForkFromMessageSemantics가 restoredDraftText를 만들고, 이것이 자식 세션 초안에 기록됩니다.)
최신에서 포크 (지금부터 분기)
정확한 포크 지점이 중요하지 않다면 최신에서 포크도 유용합니다.
실제로는 "지금부터 병렬 스레드 시작" 동작입니다.
자식 세션에서 맥락은 어떻게 되나
포크 후에는:
- 조상 메시지가 읽기 전용 맥락으로 보인다
- 구분선 행("…에서 포크됨")이 삽입된다
- 잘린 지점에서 부모 세션을 열 수 있다
(구현 근거: 조상 메시지는 injectForkContextRows로 주석이 달리고, 구분선 행은 ForkDividerRow가 렌더링합니다.)
포크 후에 보이는 것
자식 트랜스크립트에 Happier가 구분선 행을 삽입합니다.
- "…에서 포크됨"
- 잘린 지점에서 부모를 여는 버튼
내부적으로 injectForkContextRows가 조상 메시지를 읽기 전용 맥락으로 표시하고, ForkDividerRow가 "부모 열기" 딥 링크를 제공합니다.
문제 해결
"Fork from this message" 버튼이 보이지 않음
흔한 원인:
- 이 화면에서 세션/프로바이더가 메시지에서 포크를 지원하지 않음
- 포크 지점으로 쓸 수 없는 메시지 유형을 선택함
메시지에서 포크를 할 수 없다면 최신에서 포크(또는 리플레이/새 세션)를 쓰세요.
포크한 세션이 맥락을 잃은 것 같음
보통 포크 메커니즘이 예상보다 작은 초기 맥락으로 리플레이에 폴백했다는 뜻입니다.
해결 방법 두 가지:
- 더 일찍 포크(조상 맥락을 더 포함)
- 포크하기 전에 부모 세션에서 체크포인트 요약 요청
새 분기 전체가 아니라 지시 하나만 다시 하고 싶음
메시지에서 포크 + 복원된 초안이 바로 이를 위한 것입니다. 사용자 메시지 앞에서 포크하고, 복원된 초안을 편집해 다시 보내세요.
FAQ
포크하면 원래 세션이 삭제되나요?
아니요. 포크는 자식 세션을 만들고 원래 세션은 그대로 둡니다.
수정을 보내는 대신 포크해야 할 때는?
수정이 전체 방향을 바꾸거나 위험한 일을 하려는 참이라면 포크하세요.
작은 설명이라면 후속 메시지만 보내면 됩니다.
흔한 실수
- 너무 늦게 포크함(실제로는 더 이른 포크 지점이 필요했음)
- 메시지에서 포크가 필요한데 최신에서 포크를 씀
- 무엇이 바뀌었는지 팀원에게 알리지 않고 포크 결정을 알림