Port 938 upstream t3code commits into the Supaterm fork with per-commit triage and parallel worktrees
README 링크에서 발견 · yesterday
938개의 업스트림 커밋을 검토하고 포크에 이식하여 포크 기능을 유지했습니다.
What they did
Supaterm은 이전 포팅 시도가 되돌려진 후 업스트림 t3code(v0.1.3부터 v0.1.5, 그리고 32ecd71726까지의 nightly)보다 938 커밋 뒤처져 있었다. 메인테이너는 전체 포팅을 요청했고, 938행 인벤토리와 39개 결정 항목을 검토한 뒤 범위를 승인했으며, 미결 항목에 대해서는 "do what u think make sense"라고 말한 뒤 작업을 /full-stop으로 실행했다. 코드가 변경되기 전에 모든 업스트림 커밋을 포크와 대조해 검토하고 Bring, Adapt, Skip, Decide로 분류했다. 커밋은 업스트림을 이름만 바꾼 복사본(t3를 Supaterm으로, 업스트림의 "Settled"를 포크의 "Done"으로)에서 업스트림 순서대로 체리픽했기 때문에, 충돌은 포크가 실제로 분기한 지점에서만 나타났고 각 충돌은 포크 동작 쪽으로 해결했다. 포팅된 커밋은 업스트림 작성자를 유지하고 `Upstream: <sha>` 트레일러를 붙였으며, 포크 전용 수정과 적응은 별도의 `fix:`/`refactor:` 커밋으로 두고, 마지막에 현재 main을 병합했다. 결과: 733개 커밋 포팅, 203개는 근거와 함께 스킵, 2개는 부분 포팅. 두 번째 라운드에서는 스킵 및 부분 처리된 모든 행을 다시 검토해 아무것도 남기지 않았고, 병렬 워크트리에서 실행한 뒤 다시 병합했다: 업스트림의 18k줄 데드 코드 삭제를 server/packages, web/desktop, mobile에 걸쳐 분할하고 슬라이스 전반에 걸친 knip 스윕; 포크의 appCommandBus를 대체하는 단일 커맨드 디스패처; 모바일의 라우트별 새 작업 흐름 상태, blur 시 닫히는 공유 미디어 리스, 뒤로 가기 후 검색 재개, 앱 링크 파이프 파싱; Android의 중앙 정렬 및 북 모드 시트; 열리지 않는 데스크톱 빌드가 더 이상 게시되지 않도록 하는 릴리스 가드와 Node.js 다운로드를 재시도하고 검증하는 Windows CLI 빌드; 그리고 outbox 변경 이후 남아 있던 포크 전용 무효 코드 삭제. 포크 기능은 그대로 유지됐다: 고정 메시지, 다음 프롬프트 제안, 분할 창, 음성 입력, Central Icons, 그리고 T3 Connect 및 유휴 자동 압축 제거. 검증은 병합된 브랜치에서 `node scripts/check-changed.ts origin/main`을 실행했고(포맷, 린트, 두 번의 knip 패스, 36개 패키지 전체 타입체크, 변경 사항 옆의 테스트, React Compiler 커버리지), 포팅된 커밋 40~80개마다 스윕을 돌리며 각 회귀를 해당 커밋으로 이분 탐색해 원인에서 수정했다. Android 디버그(arm64)와 iOS 시뮬레이터 Debug 빌드가 성공했고, 웹 개발 서버를 격리된 상태로 실행해 stash 단축키, Cmd+K, Cmd+B, Cmd+Shift+O, 스레드 찾기, 승인을 거치는 Replay 쇼케이스 턴, 그리고 서버 오류 없이 Settings를 검증했다.
How it went
733개 커밋 이식 완료, 203개는 근거와 함께 건너뜀, 2개는 부분 완료. 라운드 2 헤드에서 전체 CI 통과, Android 및 iOS 네이티브 빌드 성공, 웹 dev-server 점검에서 단축키, 스레드 찾기, 승인 플로우, Settings를 오류 로그 없이 실행 확인. 교차 제공자 리뷰에서 라운드 2의 버그 패스에서 결함이 발견되지 않았고, 품질 관련 발견 사항은 수정 완료, 그중 하나는 이전 코드에서 실패하는 테스트와 함께 처리. Android 및 iOS 수동 기기 점검, 첫 nightly의 데스크톱 실행 확인, Force stop, 크기 조절 가능한 파일 탐색기, 칩 색상, F6 내비게이션에 대한 실제 클라이언트 확인이 남아 있음.
Worth knowing
행별 결정과 적응 관련 메모는 저장소 외부에 유지되는 원장(ledger)에 기록되므로, 트리아지 근거는 저장소만으로는 재현할 수 없습니다. 새로운 데이터베이스 마이그레이션(002_ThreadMessageSearch, 메시지 텍스트 크기의 약 3배인 FTS 인덱스, 003_ActiveTurnItemIndexes)이 포트와 함께 제공되며, 네이티브 의존성이 변경되었습니다(react-native-screens는 supacode.48로 재패키징, enriched-markdown은 supacode.19로, expo-sharing은 Android에서 소스로부터 빌드, 새로운 keyboard-controller, expo-audio 및 expo-video 패치, 그리고 highlighter 모듈 추가). 따라서 새로운 네이티브 클라이언트가 필요합니다. 동작 변경 사항으로는 페어링 링크가 저장된 경로에서 머신이 응답한 경우가 아니면 새 주소를 더 이상 연결하지 않는 점, GitHub 토큰은 구성된 호스트에만 전송되는 점, Escape가 스레드 선택보다 스레드 찾기를 먼저 닫는 점이 있습니다. 실행 간 릴리스 캐시, 외부 PR 모바일 미리보기 워크플로, 남아 있는 원시 키 리스너는 의도적으로 제외되었으며, 큐 드래그 재정렬은 결정에 따라 제거된 상태로 유지됩니다.
Wiring
Supaterm 포크 내부에서 Claude Opus 5.5와 Claude Code 하네스를 사용해 포팅을 진행했으며, 커밋 범위별로 서브에이전트를 활용하고 2차 라운드에서는 병렬 git worktree를 사용한 뒤 하나의 브랜치로 다시 병합했습니다. 업스트림은 이름을 바꾼 복사본으로 추가했고(t3는 Supaterm으로, "Settled"는 "Done"으로), 그 덕분에 실제 분기 지점을 제외하면 cherry-pick이 깔끔하게 적용되었습니다. 리뷰는 Codex와 GPT-6.1 Sol을 xhigh effort로 실행해 두 라운드 모두 버그, 보안, 코드 품질을 점검했습니다. 검증에는 저장소 자체의 `node scripts/check-changed.ts origin/main` 게이트(포맷, 린트, 두 차례의 knip 패스, 36개 패키지 타입체크, 인접 테스트, React Compiler 커버리지)를 사용했고, 추가로 Android 디버그 및 iOS 시뮬레이터 빌드와 격리된 상태를 대상으로 한 웹 개발 서버도 활용했습니다. 메인테이너는 938행 인벤토리와 39개 결정 항목을 바탕으로 범위를 승인했으며, 작업은 /full-stop으로 진행되었습니다.
Try it yourself
I can't execute this workflow for you — I don't have access to your repository, your fork, the upstream remote, or the ability to run git, build tools, or a dev server. What I can do is give you a concrete, runnable playbook that implements each of your nine steps, with the exact commands and decision rules, so you (or an agent with repo access) can execute it.
---
## 0. Setup
```bash
git remote add upstream https://github.com/<org>/<repo>.git
git fetch upstream --tags
git fetch origin --tags
UPSTREAM_HEAD=$(git rev-parse upstream/main)
echo "upstream HEAD: $UPSTREAM_HEAD"
```
## 1. Find the last synced upstream commit
```bash
# Merge history: look for merge commits that brought upstream in
git log --merges --oneline origin/main | grep -i upstream
# Reverted port PRs: search for reverts of upstream SHAs
git log --oneline --grep='Revert' origin/main | head -50
git log --oneline --grep='Upstream:' origin/main | head -50
# Best signal: the newest commit on origin/main that carries an Upstream: trailer
LAST_SYNCED=$(git log origin/main --grep='^Upstream: ' --format='%b' \
| grep -oE 'Upstream: [0-9a-f]{7,40}' | awk '{print $2}' \
| while read s; do git merge-base --is-ancestor "$s" upstream/main && echo "$s"; done \
| tail -1)
echo "last synced: $LAST_SYNCED"
# Enumerate the gap
git log --reverse --oneline "$LAST_SYNCED"..upstream/main > /tmp/upstream-gap.txt
wc -l /tmp/upstream-gap.txt
```
Cross-check against `git log --merges` and any reverted port PRs — if a revert exists, the effective sync point is the commit *before* the reverted batch, not the revert itself.
## 2. Triage ledger (write inside the repo)
Create `docs/upstream-port/ledger.md` (or wherever the repo keeps docs) with one row per commit:
```markdown
| # | Upstream SHA | Subject | Class | Reason |
|---|--------------|---------|-------|--------|
| 1 | abc1234 | fix: null check in parser | Bring | Pure bugfix, no fork divergence |
| 2 | def5678 | feat: new auth provider | Adapt | Touches files the fork renamed |
| 3 | 9012abc | chore: bump eslint | Skip | Fork pins a different eslint major |
| 4 | 3456def | refactor: split router | Decide | Overlaps fork's own router rewrite — needs owner call |
```
Classification rules:
- **Bring** — applies cleanly, no fork divergence in touched files.
- **Adapt** — real change, but files/paths/APIs were renamed or restructured in the fork.
- **Skip** — superseded by fork work, or irrelevant (e.g. CI for a platform the fork dropped).
- **Decide** — genuine conflict of intent; needs a human call before porting.
Commit the ledger before touching code so the decisions are reviewable in the PR.
## 3. Cherry-pick from a renamed upstream copy
```bash
# Build a renamed mirror of upstream so conflicts only appear where the fork really diverged
git checkout -b upstream-renamed upstream/main
# Apply the fork's rename mapping (script it — sed over paths, package names, imports)
./scripts/apply-fork-renames.sh
git commit -am "chore: apply fork rename mapping to upstream mirror"
# Then cherry-pick in upstream order onto a port branch
git checkout -b port/upstream-sync origin/main
while read sha _; do
git cherry-pick -x "$sha" || {
echo "CONFLICT on $sha — resolve toward fork behavior, then:"
echo " git add -A && git cherry-pick --continue"
break
}
# Ensure the trailer is present
git commit --amend --no-edit -m "$(git log -1 --format=%B)
Upstream: $sha"
done < /tmp/upstream-gap.txt
```
Rules:
- Resolve every conflict **toward the fork's behavior** — the fork is the source of truth for divergent code.
- Keep upstream authorship (`git cherry-pick` preserves it; `-x` adds the `(cherry picked from …)` line — keep both that and your `Upstream: <sha>` trailer).
- Fork-only fixes go in separate `fix:` / `refactor:` commits, never folded into a port commit.
## 4. Preserve fork features explicitly
Before starting, write `docs/upstream-port/fork-features.md`:
```markdown
- [ ] Feature A: <files, entry points, tests>
- [ ] Feature B: ...
```
After the port, for each item run its tests and a manual smoke check. Any regression is a port bug — fix it in a `fix:` commit, not by reverting the port.
## 5. Second pass over Skip / partial rows
```bash
# Parallel worktrees, one slice per reviewer
for slice in auth router ui cli; do
git worktree add ../wt-$slice port/upstream-sync
done
```
In each worktree, re-read every `Skip` and every `Adapt` row and confirm the upstream intent is either present or genuinely superseded. Merge findings back as `fix:` commits on `port/upstream-sync`. Update the ledger with a `Verified` column.
## 6. Changed-files check every 40–80 commits
```bash
# After each batch
pnpm -w format:check
pnpm -w lint
pnpm -w knip
pnpm -w -r typecheck
# tests beside changed files
git diff --name-only "$BATCH_START"..HEAD | grep -E '\.(test|spec)\.' | xargs pnpm test --
```
On regression: `git bisect start HEAD "$BATCH_START"` → `git bisect run <check>` → fix the cause in a `fix:` commit.
## 7. Native builds + dev server smoke
```bash
# Android debug
cd apps/mobile && npx expo run:android --variant debug
# iOS simulator Debug
npx expo run:ios --configuration Debug --simulator "iPhone 15"
# Dev server against isolated state
export DATA_DIR=$(mktemp -d)
pnpm dev 2>&1 | tee /tmp/dev.log
```
Exercise: main shortcuts, search, an approval flow, settings. Then `grep -iE 'error|warn' /tmp/dev.log` and confirm clean.
## 8. Independent review
Run a separate reviewer (human or agent) against `port/upstream-sync` for bugs, security, code quality. For every behavioral fix, add a test that fails on the pre-fix commit:
```bash
git stash && pnpm test -- <new-test> # must fail
git stash pop && pnpm test -- <new-test> # must pass
```
## 9. Report
Produce `docs/upstream-port/report.md` with:
- **Manual device checks still needed** — anything the simulator/emulator can't cover (push notifications, deep links on real hardware, biometrics, background sync).
- **User-visible behavior changes** — DB migrations, native dependency bumps, auth flow changes, link/deep-link handling changes, config/env var changes.
---
If you paste the output of step 1 (`git log --reverse --oneline $LAST_SYNCED..upstream/main`) and the fork's rename mapping, I can help you draft the actual ledger rows and flag which commits are likely `Decide`.