병렬 로컬 CI 운영 — v0.17.7
2026-09-10 #1491 승인 정책 (v0.17.8 대상): 일반 작업→release는 precheck 후 Merge Check만 수행하고, 최종 release→staging에서 full을 실행한다. staging→main은 동일 tree의 유효한 full 기록과 현재 staging 배포·smoke를 확인한다. 진단용 dry-run·부분 재현은 유지한다. 앱 main
6afe58ec설치를 확인했다. 정상 큐의 새 실행·운영 배포 결과는 적용 기록에서 확인하며 아래 과거 실행 이력과 구분한다.
전체 CI·release 병합 완료, Production 미반영: #1464는 통합 앱 PR #1484로
release/v0.17.7에 병합됐다. 전체 CI는 7분 32초에 성공했고 브라우저 56건·화면 22건 모두 fail/skip/flaky 0이다. staging PR #1488도 병합됐지만 이 기록 시점의 main·Production 반영은 아직이다. 작업 기록과 문서 PR #2를 함께 관리한다. 원래 앱 PR #1467은 준비 이력이다.
적용 범위와 전환 상태
아래 Phase 4~7 문단의 미반영·실패·인계 상태는 당시 관측이다. 최종 결과는 전체 CI·release 병합과 적용 판정을 따른다.
2026-09-09 Phase 4 완료 보고 기준 release/v0.17.7은 84e2d7d다. 이 문서의 병렬 실행은 해당 release에 아직 반영되지 않았다. 기존 절차는 PR 전 로컬 CI와 릴리스 랜딩 큐를 함께 읽는다.
명령 개편 #1460은 release/v0.17.6에 병합됐지만 release/v0.17.7에는 아직 도달하지 않았다. 따라서 이 대상 release에는 ci:precheck-local·merge:request가 없다. 전환 전에는 실제 checkout에 설치된 명령과 기존 ci:local·landing:request 경로를 사용한다. 새 이름을 문서에 적었다고 명령이나 원격 workflow가 활성화되는 것은 아니다. #1464 작업 브랜치의 ci:full-local은 전체 로컬 CI 진입점이며, 일반 전체 실행은 release 큐만 수행한다.
문서·관리자 분리 #1463·PR #1466도 진행 중이다. 문서는 이 저장소에서 관리하며, 앱 checkout에 남아 있는 과거 관리자 결합 빌드와 큐의 문서 작업은 분리 작업의 적용 상태와 구별한다. #1464가 분리 작업까지 완료한 것으로 해석하지 않는다.
Phase 6 통합 준비 — 2026-09-09 후속
이후 확인한 앱 main은 df394f77이며 #1460의 새 명령이 포함됐다. 오너가 승인한 HQ가 다른 승인 변경까지 합친 고정 후보 5792496b6adc788e5266234ac56365136eb98f4e를 전달했고, 독립 브랜치 codex/issue-1464-hq-integration에서 #1464를 연결했다. 전달 커밋은 a105313e54ccd5177258ef666810af1254ac068e, tree는 6058983952815dc7c7c74cfdab8e19b824e95a17이다. 이 커밋은 HQ 전달용 로컬 산출물이며 원격 PR #1467의 head fb94e0ce나 release 반영을 뜻하지 않는다.
새 ci:precheck-local·ci:full-local 진입점, 큐 표시, DB fixture 검사와 기존 병렬 실행·샌드박스 정리를 함께 보존했다. 사전 검증은 정적 검사·단위 2조각과 서버 변경에 필요한 DB 검사만 계획하며 브라우저를 실행하지 않는다. 정적·단위 검사에는 큐가 전달한 준비 전 DB 루트가 상속되지 않고, DB를 쓰는 검사는 실제 준비된 자기 환경을 받는다. --plan은 release를 작업 브랜치에 합치지 않는다.
main 기반 첫 준비본에서는 브라우저 설정·산출물·배포 대상 21개와 진입점·precheck 구성·DB 환경·keep/prune·큐 준비 순서 7개가 통과했다. 실제 HQ 후보와 합친 뒤에는 변경에 필요한 브라우저 설정 6개와 검사 목록·표시 이름·문서 checkout 없는 큐 준비/정리 5개만 추가로 통과했다(후자 2.589초). 두 준비본에서 겹치는 검사를 고유 검사 수로 합산하지 않는다. 수정한 실행기·검사의 ESLint와 문법 검사도 통과했고 실제 계획 출력은 작업 트리를 바꾸지 않았다. 오너의 CI 최소화 지시에 따라 새 full·hosted full·dry-run·큐 요청·push는 만들지 않았다.
#1463의 문서·관리자 검사 제거, #1468의 직접 Node DB 호출 시간 제한 등록과 Playwright 케이스 60초·동작 10초·재시도 0을 보존했다. #1470의 표시 이름도 브라우저 조각과 viewport의 준비·실행·요약에 연결했다. #1476의 결과 재사용 연결은 별도 담당 범위이며 이 변경에 중복 구현하지 않았다. 원래 PR·브랜치는 유지하며 최종 전체 검증·승격은 HQ가 조정한다.
Phase 7 첫 통합 full 실패 수리 — 2026-09-10
첫 통합 full 실행 34364544193은 CI 본체 7분 37초, Actions job 9분 16초로 실패했다. PR #1484의 frozen head는 5c460a14fbd961eb35018e698df3b9235ce8e4d1이다. 정적 검사·단위 두 번째 조각·DB 검사·viewport 22건은 통과했지만, 단위 첫 번째 조각의 시간 제한 장부 검사 2건과 브라우저 CASE-015/029/041이 실패하고 CASE-039/040은 건너뛰었다. 이 실행으로 정상 full 5분 달성을 선언하지 않는다. 다른 담당자의 실패 수리는 해당 담당 범위로 남겼다.
#1464 담당 범위의 CASE-039/040은 새 browser-4 캐시 아래 구버전 worktree를 만들다가 긴 SQL 경로 2개에서 Filename too long으로 실패했다. CI의 Git 전역·시스템 설정 격리 때문에 사용자 core.longpaths 설정도 상속하지 않았다. 실제 v0.17.1 태그와 같은 긴 경로·격리 환경에서 원본 Git 명령은 exit 128, 명령에만 -c core.longpaths=true를 더하면 checkout과 제거가 모두 exit 0이었다. 짧은 가짜 소스 fixture에서는 놓쳤던 사전 검증 누락이다. 실제 큐가 삭제한 임시 merge SHA와 인증 fetch까지 그대로 재현한 것은 아니다.
수리는 worktree 생성·제거 Git 명령에만 긴 경로 지원을 적용하며 전역 설정을 변경하지 않는다. builder는 진행률에 가려지던 실제 error/fatal 행을 보존한다. 브라우저 요약에는 skip 사유를, 필수 증거 검사 결과에는 실제 거부 메시지를 남긴다. skip과 완료·정리 증거 누락을 통과로 바꾸지 않으며, 캐시의 태그·입력·환경·산출물 해시 검증도 유지한다.
로컬 전달 커밋은 de95439c8d2e80763cc736c5c95029ce4c54f079(실패 사유)와 6b05790f610543c53556934c4bfbcf8d8cf79c87(긴 경로), 최종 tree는 4c5a6de10c2bcd24b2f338fea2bc5def14e17a7e다. 실패 사유 계약 검사 4건과 구버전 캐시 격리 검사 2건이 통과했다. 긴 경로 검사는 실제 Git에 source 240자·lane 260자 초과 SQL 파일을 넣어 원본 builder 실패 → 수정 후 성공을 확인하고, 파일 내용·캐시 격리·임시 worktree 등록 제거까지 검사했다. 수정 파일의 ESLint와 diff 검사도 통과했다.
HQ가 승인한 좁은 실제 재현은 이 최종 clean head에서 수행했다. v0.17.1 태그 58e9b047eea319e2b1bae986d355ab5158d41c91의 실제 번들 준비는 6.740초에 성공했고 --print의 출처·캐시 검증은 0.639초에 통과했다. 임시 legacy worktree 등록은 남지 않았다.
같은 격리 DB 하나에서 CASE-039/040을 한 번 실행한 결과는 0 pass / 2 fail / 0 skip / 0 flaky, 브라우저 명령 58.276초다. 준비 skip은 제거됐지만 CASE-039는 반복 복구의 두 번째 재수화를 10초 안에 기다리다가 실패했다(실제 태그의 복구 정책에는 최대 30초 probe 지연과 연속 성공 2회가 필요). CASE-040은 새 탭에서 달력 기록을 누른 뒤 상세 화면이 10초 안에 나타나지 않아 실패했고, 원인은 아직 확정하지 않았다. 두 케이스 모두 완료 증거를 얻지 못했으며 필수 증거 검사는 이를 계속 거부했다. HQ 지시로 두 케이스의 로그·trace·실행 진입점을 #1468 담당에게 인계했다.
실행 phase7-legacy-cases-1788966448554의 소유 프로젝트 cil348c7884는 Edge 종료 후 supabase stop --no-backup exit 0(5.508초), 컨테이너·네트워크·볼륨 잔여 0을 확인했다. 원본 연결만 해제하고 샌드박스 폴더를 제거했으며 보고서와 trace는 보존했다. DB 정리는 #1464 담당이 끝냈고 인계 뒤 새로 띄우지 않았다. HQ는 두 수리 커밋을 통합 후보 9639e1d0에 반영했다고 전달했다. 이 상태는 부분 수리·실패 재현·자원 정리 완료이며, full 성공이나 release·Production 반영 완료가 아니다. 전체 check/full·큐 요청·별도 PR·push는 추가하지 않았다.
전체 CI·release 병합 — 2026-09-10
HQ가 후속 수리를 합친 실행 34372922344은 7분 32초에 전체 통과했다. 검증한 merge commit e56604cb0ae4d9fcaa361fbd99c3978dabb60535가 PR #1484를 통해 release/v0.17.7에 반영됐다(01:00 KST). 정적·단위 2조각·DB reset·schema·pgTAP 130파일/2,328 assert·동시 저장·통계 격리와 비브라우저 E2E가 통과했다. 브라우저는 4조각 각각 14 pass, viewport는 22 pass이고 모두 fail/skip/flaky 0이며 필수 완료 증거도 통과했다. 앞선 CASE-039/040 실패의 후속 수리는 #1468 담당 결과와 함께 통합된 것이며 #1464의 번들 수리 하나로 본문 문제까지 해결했다고 보지 않는다.
staging PR #1488의 merge commit은 213aaeb7c21598e3023cf47d16ec756feabad80a(01:02 KST)다. staging 배포 34374171430는 기록 시점에 진행 중이었다. Production 완료와 trusted main의 큐 전 자동 prune 활성화는 아직 선언하지 않는다. 이 문서 마무리를 위해 앱 full·큐 요청을 다시 실행하지 않았다.
병렬 실행 구성
기존 로컬 CI는 하나의 DB와 순차 실행에 묶여 브라우저 여정이 길어졌다. #1464는 독립 검사를 동시에 진행하면서 DB를 쓰는 검사는 각각의 격리 환경에서 실행한다. 브라우저 프로세스 안의 작업자 수는 늘리지 않는다.
| 실행 묶음 | 기본 구성 | 의존 관계·격리 |
|---|---|---|
| 정적 검사·기존 빌드 검사 | 1개 묶음 | 기존 검사와 산출물 검증을 유지한다. 단위 테스트 및 DB 준비와 병렬 실행한다. |
| 단위 테스트 | 2조각 | 같은 테스트 집합을 둘로 나눈다. 각 조각의 실패와 조건부 skip을 따로 보고한다. |
| 브라우저 여정용 빌드 | 공유 준비 단계 1회 | 정적 검사 묶음 이후 앱 여정 번들을 한 번 준비한다. 브라우저 조각과 viewport는 이 산출물을 공유한다. 관리자 검사는 별도 문서·관리자 저장소가 소유한다. |
| DB·비브라우저 검사 | DB 1개 | 마이그레이션·schema·pgTAP·동시 저장·통계 격리 검사를 수행하고, 선행 DB 검사가 통과하면 선택된 비브라우저 E2E를 실행한다. |
| 번호 CASE 브라우저 여정 | 기본 4조각 | 조각마다 DB·Auth·Edge·미리보기 서버를 격리한다. 공유 빌드와 자기 DB 준비가 끝나면 시작한다. |
| viewport 검사 | DB 1개 | 브라우저 여정 DB와 분리한다. 공유 빌드와 자기 DB 준비가 끝나면 시작한다. |
브라우저 조각 수는 --shards로 1~8 사이에서 지정하며 기본값은 4다. 기본 전체 실행의 DB는 DB·비브라우저 1 + 브라우저 4 + viewport 1 = 6개다. 각 Playwright 실행은 workers: 1과 기존 테스트의 순차 실행 규칙을 유지한다. 조각 수를 늘리면 DB 컨테이너·메모리·포트 사용량도 늘어나므로 처리 시간이 비례해 줄어든다고 가정하지 않는다.
브라우저별 반복 빌드는 E2E_SKIP_BUILD=1로 생략한다. 여기서 “한 번 빌드”는 브라우저 여정용 공유 준비 단계를 뜻한다. 정적 검사에 이미 포함된 앱·관리자 빌드와 산출물 검증까지 없애거나 전체 과정의 Vite 호출이 한 번뿐이라고 뜻하지 않는다.
선행 검사가 실패하면 그 결과에 의존하는 단계는 시작하지 않는다. 관련 없는 검사는 끝까지 실행해 진단 결과를 남기며, 전체 결과는 실패를 유지한다.
DB·포트·캐시의 소유 범위
각 실행 묶음은 레포 밖 샌드박스, 서로 다른 Supabase project ID, API·DB·shadow 포트를 사용한다. 포트는 프로세스를 시작하기 전에 배정하고, Docker가 이미 공개한 포트도 확인한다. 브라우저와 viewport의 미리보기 서버도 독립 포트를 사용하며 strictPort로 다른 포트에 조용히 뜨는 상황을 막는다.
main 기반 통합 준비 버전에서 큐의 첫 브라우저 미리보기는 4173을 사용하고, 나머지는 다른 빈 포트를 할당한다. 세션의 부분 재현은 작업트리 경로로 정한 4300~4399의 시작점에서 빈 포트를 찾는다. 큐의 첫 포트가 이미 사용 중이면 준비 실패로 처리한다.
새로 만드는 DB 검사 묶음은 빈 마이그레이션 디렉터리로 Supabase를 먼저 기동하고, 실제 마이그레이션 연결을 복원한 뒤 필수 db reset --local --no-seed에서 전체 마이그레이션을 한 번 재생한다. 기동 직후 같은 마이그레이션을 reset으로 다시 재생하던 중복을 제거하는 변경이다. 실제 마이그레이션·schema snapshot·pgTAP 검사를 생략하지 않는다. 브라우저 환경은 이 DB 검사 묶음과 별도로 준비한다.
구형 탭 CASE-039/040의 번들도 E2E_LEGACY_BUNDLE_CACHE로 실행·묶음별 캐시 경로를 받는다. 같은 태그라도 다른 묶음의 캐시를 삭제하거나 교체하지 않는다. 각 묶음은 workers: 1이므로 자기 캐시 갱신은 순차 실행한다. 태그·현재 도구·빌드 입력·환경·산출물 해시 검증은 유지한다. 이 환경변수를 지정하지 않은 독립 실행의 기존 전역 캐시 동작까지 바꾼 것은 아니다.
Supabase CLI 상태도 SUPABASE_HOME=<각 샌드박스>/.supabase-cli로 분리한다. start·status·reset·test·E2E 하위 명령·Edge serve·stop·prune가 같은 샌드박스의 경로를 상속한다. Supabase 2.113.0은 telemetry 이벤트 전송을 꺼도 상태 파일을 쓰므로 SUPABASE_TELEMETRY_DISABLED=1에 더해 HOME 격리가 필요하다. 사용자 홈의 설정·자격증명 파일은 복사하지 않는다. 공식 HOME 설정 설명과 telemetry 상태 구현을 기준으로 확인했다.
Docker 종료는 순차 실행한다. 검사는 병렬로 수행하되, 시작한 작업이 모두 끝난 뒤 해당 실행의 샌드박스를 하나씩 종료한다. Edge Functions는 Windows에서 직접 시작한 프로세스와 그 자식들의 종료를 기다린 뒤 DB 정리로 넘어간다. 다른 작업자의 DB나 서비스 환경을 종료하지 않는다. 정리에 실패하면 실패한 샌드박스와 로그를 결과에 남기고 전체 통과로 처리하지 않는다.
샌드박스 사용과 유휴 정리
다음은 #1464 변경이 있는 앱 checkout에서 사용하는 명령이다. 기본 샌드박스 루트는 OS 임시 폴더의 barbelic-ci-local/<체크아웃 해시>이며 그 아래에 database·browser-N·viewport가 만들어진다. --sandbox "<샌드박스 루트>"로 명시한 경우 종료할 때도 같은 루트를 지정한다.
| 목적 | 명령·동작 |
|---|---|
| 브라우저 부분 재현 후 종료 | npm run ci:full-local -- --only browser --shards 4 --base origin/release/v0.17.7 |
| 재현 결과를 더 살펴보려고 유지 | 위 부분 실행 명령에 --keep을 붙인다. 명시한 경우에만 스택을 유지하며 큐의 full 실행에는 허용하지 않는다. |
| 유지한 기본 샌드박스 종료 | npm run ci:local -- --stop. 명시 루트를 사용했다면 --sandbox "<같은 루트>"도 붙인다. |
| 유휴 관리 샌드박스 회수 | npm run ci:local -- --prune. 마지막 검사 사용 후 기본 6시간 이상 지난 항목을 종료하고 폴더도 제거한다. |
| 유휴 기준을 12시간으로 변경 | npm run ci:local -- --prune --max-idle-hours 12 |
부분 실행은 성공·실패·중단 모두 기본 종료 경로를 거친다. 일반 종료는 샌드박스 폴더를 남기며, 유휴 prune가 나중에 폴더까지 회수한다. --keep의 마지막 사용 시각은 검사 종료 시점으로 갱신한다. DB 내부 cron이나 파일 갱신은 사용자 검사 활동으로 세지 않는다. 별도 타이머가 6시간 정각에 동작하는 구조가 아니라, 다음 수동 prune 또는 큐 full 전 prune에서 경과 시간을 판단한다.
큐 전 자동 prune의 활성화는 별도다. release-merge-request.yml은 trusted main의 release-landing-service.mjs를 실행한다. 후보의 ci-local.mjs에 포함된 사용 후 자동 종료는 후보에서 동작하지만, full 전에 prune를 호출하는 서비스 변경은 main에 도달한 뒤 자동 호출된다. 후보 코드와 컨트롤러 양쪽의 적용 상태를 확인하며, 이 문서만 게시했다고 자동 prune가 활성화된 것으로 기록하지 않는다.
관리 등록과 샌드박스 표시에 원본 repository·샌드박스의 절대 경로, project ID, owner PID, run ID, 마지막 사용 시각을 기록한다. 유휴 정리는 활성 프로세스, 관리 범위 밖 경로, 소유 정보가 불명확한 폴더를 건너뛰고 사유를 출력한다. 원본 repository를 포함하거나 그 안에 있는 경로도 삭제하지 않으며 migrations·tests·functions junction은 연결만 제거한다.
새 실행의 취득과 prune의 취득은 같은 샌드박스 단위로 원자적으로 처리한다. 정리 도중 새 실행이 같은 환경을 잡아 stop·삭제와 겹치지 않는다. 취득 소유 PID가 죽었고 소유 토큰이 그대로임을 확인한 경우에는 남은 취득을 복구하지만, 정보가 없거나 살아 있는 소유자는 보호한다. 전역 Docker prune나 다른 프로젝트 정리는 수행하지 않는다.
등록 없는 기존 폴더를 prune가 자동으로 소유한다고 추정하지 않는다. 다만 사용자가 이번 실행에서 명시적으로 사용할 정확한 환경은 경로 해시에서 계산한 project ID·설정·현재 repository의 세 junction이 모두 일치하고 실행 중인 Docker 자원이 없을 때 사용 시점에 등록할 수 있다. 구조 불일치·실행 중·조회 실패는 보호한다. 큐가 이미 임시 작업 폴더를 지운 경우에는 해당 폴더와 정확한 project ID의 컨테이너·네트워크·볼륨이 모두 없다는 읽기 확인 후 남은 등록만 제거한다.
보고서와 완료 증거
보고서는 test-results/ci-local/<runId>/ 아래에 실행별로 보존하며 브라우저 조각과 viewport는 각자의 하위 디렉터리를 사용한다. 각 묶음의 로그와 summary.json에는 실패·skip·소요 시간과 이번 실행의 commit·base·부분 실행 여부를 남긴다.
| 자료 | 저장·판정 방식 |
|---|---|
| 브라우저 JSON·추적·HTML | 각 조각의 E2E_OUTPUT_DIR 아래에 저장한다. 다른 조각의 파일을 덮어쓰지 않는다. |
| viewport JSON·추적 | viewport의 별도 E2E_OUTPUT_DIR에 저장한다. |
| 호환 JSON | 브라우저 조각을 합쳐 최상위 test-results/error-cases.json에서도 읽을 수 있게 한다. 실패·재시도·전역 오류·첨부 경로를 보존하며 누락·중복 조각과 실행 정보 혼합을 거부한다. |
| 필수 완료 증거 | 합친 JSON만 믿지 않고 현재 run 디렉터리의 각 묶음 증거를 직접 검사한다. 이전 실행의 형제 디렉터리는 통과 근거로 섞지 않는다. |
필수 증거는 정확한 commit·run, 전체 사전 목록, 조각의 완전성, 첫 시도 통과, 실제 사용자 여정과 cleanup 완료를 확인한다. 실패·skip·기대한 실패·재시도 후에만 통과한 결과·누락·오래된 증거는 성공으로 바꾸지 않는다. 단위 테스트의 조건부 skip 집계와 필수 브라우저 증거의 skip 거부는 서로 다른 판정이다.
실행 권한과 부분 재현
전체 ci:full-local은 명시적 merge:request --dry-run 진단 요청의 큐가 만든 고정 통합 후보에서 실행한다. 일반 release 병합 요청은 full·DB 스택 준비를 하지 않으며 full 성공 기록도 만들지 않는다. 개발 세션은 --only로 필요한 실패 단계만 재현할 수 있다. 부분 실행의 통과·실패나 계획 출력은 full CI 통과 증거가 아니다. 큐 환경 표시를 개발 세션에서 임의로 설정해 전체 실행 제한을 우회하지 않는다.
전환 전 명령의 실제 존재 여부는 checkout의 package.json과 원격 workflow를 기준으로 판단한다. --plan은 계획만 출력하며, 큐 요청의 --dry-run은 실제 full CI를 수행하므로 같은 기능으로 취급하지 않는다. 최신 base+head·최종 tree 확인과 검증한 커밋 그대로 병합하는 기존 큐 규칙은 유지한다.
각 실행의 시간 제한·재시도 설정은 테스트 시간 제한 정본과 해당 checkout의 #1468 적용 상태를 따른다. 아래 24건 용량 실험은 retries: 0을 명시해 수행했다.
초기 탐색 측정과 한계
다음 수치는 2026-09-09 작업 중 관측이다. 서로 다른 실행 범위이며 실패가 포함된 실행 시간이다. 변경 중인 작업 트리에서 측정했으므로 최종 고정 커밋의 full CI 증거나 정상 통과한 전후 성능 비교로 사용할 수 없다.
| 측정 | 소요 시간·결과 | 해석 한계 |
|---|---|---|
| 이전 Actions 34324908737 | 전체 30분 59초. 이 중 browser 20분 34초, 44 pass / 12 fail / 1 flaky | 기존 전체 실행이지만 실패가 포함됐다. 아래 부분 실행과 직접 비교해 전체 개선율을 계산하지 않는다. |
| DB 부분 실행 | 6분 7초 → 5분 27초. pgTAP 129파일·2,322 assert, 동시 저장 13개, 통계 격리 검사 통과 | schema snapshot은 dblink 차이로 실패했다. DB 묶음 전체 통과가 아니다. |
| 브라우저 4조각 + viewport 부분 실행 | 해당 묶음 경과 13분 51초. browser 42 pass / 13 fail / 2 skip, viewport 18 pass / 2 fail / 2 skip | 정적·단위·전체 DB 검사를 포함한 full 시간이 아니다. 필수 증거 검사는 실패와 skip을 거부했다. |
이 결과로 정상 통과한 성능 기준이나 5분 달성을 선언하지 않는다. DB 초기 준비의 중복 제거와 브라우저 분할이 실제로 동작하는지 확인한 탐색 자료이며, 정상 통과하는 같은 고정 입력의 전후 full 비교는 아직 없다.
dblink snapshot과 통계 정리 관련 수정은 #1460을 통해 release/v0.17.6에 병합됐지만 이 측정 기준의 release/v0.17.7에는 미반영이다. 다른 release의 병합과 현재 대상 release의 검사 통과를 구분한다.
초기 병렬 Docker 종료에서는 3개 환경이 남아 종료를 순차 방식으로 바꿨다. 당시 조용한 실행 모드에서 오류 출력이 보존되지 않아 CLI 내부의 정확한 원인은 확정하지 않았다. 남은 환경을 정리한 뒤 새 스택 2개를 실제로 동시에 기동해 모두 exit 0, 순차 종료도 각각 exit 0을 확인했다. 이번 작업의 6개 프로젝트에 속한 컨테이너·네트워크는 잔여 0개였다. 이 결과는 해당 실행 환경의 정리 동작을 검증하며, 전체 CI 성공을 뜻하지 않는다.
4·8·12개 격리 환경의 동일 24건 비교
시험 PC는 Ryzen 9 5900X(물리 12코어·논리 24스레드), RAM 63.94GiB이며 Docker 한도는 24 CPU·31.31GiB다. 조사 시작 때 다른 작업의 DB 스택 4개가 있었고 이후 실행 부하는 변했다. 여유 자원과 처리 시간은 이 공유 환경에서의 관측이다.
2026-09-09 Phase 4에서 CASE-009 온보딩 여정 총 24건을 각 묶음에 나눠 실행했다. 각 묶음은 새 DB에 앱 마이그레이션을 적용하고 별도 Edge·브라우저를 사용했으며, 프로세스마다 workers: 1, retries: 0이었다. 공유 앱 빌드는 먼저 한 번 수행했고 아래 총시간에서 제외했다. 다른 작업도 진행되는 이 PC에서 각 조건을 한 번씩 측정했으므로 반복 평균이나 고정 부하의 full CI 비교가 아니다.
| 병렬 DB·브라우저 | DB 준비 | Edge·상태 준비 | 여정 | 종료 | 총시간 | 결과 | 남은 호스트 RAM 최저 |
|---|---|---|---|---|---|---|---|
| 4 | 126.58초 | 12.56초 | 115.42초 | 25.96초 | 280.56초 | 24 pass | 21.24GiB |
| 8 | 230.02초 | 17.47초 | 88.10초 | 53.72초 | 389.36초 | 24 pass | 17.05GiB |
| 12 | 317.31초 | 23.02초 | 140.52초 | 87.32초 | 568.24초 | 23 pass / 1 fail | 13.35GiB |
총시간은 묶음 전체의 경과 시간이어서 각 단계 값의 합과 소규모 실행·기록 비용만큼 차이가 있다. 12개에서 list_my_groups_v1의 rpc_failure 이벤트를 필수 완료 검사가 거부했다. 브라우저 구간의 호스트 CPU 평균은 87.34%였고 메모리 부족은 관측하지 않았지만, 실패와 부하의 인과관계는 확정하지 않았다. 실패를 재시도나 시간 제한 완화로 지우지 않았다.
이 표의 앞선 8개 시도에서는 Supabase 2.113.0이 공용 telemetry.json을 atomic rename하는 중 EPERM으로 실패했다. HOME 격리를 적용한 새 실행의 결과가 위 표다. 이벤트 전송 비활성화만으로 상태 파일 쓰기가 멈추지 않는 점을 확인했고, 각 환경에 독립 SUPABASE_HOME을 사용했다. 이 실패 원인은 초기 병렬 stop에서 출력이 소실돼 내부 원인이 미확정이었던 관측과 구분한다.
각 4·8·12개 묶음의 모든 스택 종료는 exit 0이었다. 앞선 실패 실험의 잔여 네트워크·볼륨도 해당 소유 프로젝트만 정리했으며 실험 샌드박스 폴더는 제거하고 보고서는 보존했다. 별도 실제 DB 유지 → 시험용 registry의 6시간 경과 판정 → 종료·폴더 제거도 통과했고 원본 마이그레이션 해시는 동일했다.
운영값은 기본 4, 선택 범위 1~8을 유지한다. 이번 짧은 여정에서는 8개의 브라우저 구간이 짧아졌지만 DB 기동과 순차 종료 비용으로 총시간은 4개보다 길었다. 12개는 실험용 직접 실행이며 지원 옵션으로 추가하지 않았다. 정상 full CI의 최적 병렬 수와 5분 달성은 아직 검증하지 않았다.
Phase 4 구현 커밋 fb94e0ce의 npm run check는 3,447 pass / 27 conditional DB skip / 0 fail이다. 조건부 DB skip은 실제 DB 통과 수에 합산하지 않는다. 용량 보고서의 revision은 측정 시작 당시 e3f83f0b이며 작업 트리의 HOME·프로세스 종료 수리를 포함해 실험했다. 이 부분 측정을 최종 고정 커밋의 full 검증 결과로 소급하지 않는다.
적용 판정
현재 상태는 전체 CI 성공·release/v0.17.7 및 staging 병합 완료, Production 미반영이다. 전체 CI 7분 32초는 5분 내외 목표를 달성한 결과가 아니다. 4·8·12 비교는 앞선 동일 24건 부분 실험이며, 서로 다른 전체 검증 범위나 실패 실행과 직접 개선율을 계산하지 않는다. 문서 게시만으로 trusted main의 큐 전 prune나 Production 배포를 완료한 것으로 바꾸지 않는다. 최종 작업 기록에 검증과 적용 경계를 정리했다.