v0.17.10 릴리스 (2026-09-11)
- 기간: 2026-09-11, Codex 1세션. 오너 요청: “1530, 1546, 1552, 1554 포함해서 17.10 릴리스 메인까지”, 미완료 작업은 기다리고 재승인 없이 main까지 진행. 별도 릴리스 이슈 #1557로 관리한다.
- 랜딩: staging PR #1559 → main PR #1562, main
e49545f15d3454b7e19a0330202139b2a7436fa9. 두 승격 full과 두 환경의 DB·Edge·앱·smoke가 성공했으며 v0.17.10 태그·Release는 같은 main을 가리킨다. 별도 Production 브라우저 사후 점검은 아래에 구분한다. - 설계서: 없음(기존 수리의 릴리스 승격).
- 정본: 릴리스 절차, 배포 파이프라인. 제품 변경의 정본은 아래 작업별 기록이다.
- 도구: Git/gh 및 기존
ci:wait. 작업 파일·원격 결과는 세션의work/에 보관하며 앱에 기록용 커밋을 만들지 않는다. - 게이트: 최종 후보의
npm run check, 작업 PR별 precheck·Merge Check, release→staging hosted full, staging 배포·smoke, staging→main의 기존 검증, Production 배포·smoke·태그와 별도 사후 브라우저 점검. - 버그리포트: 기존 BUG-105, BUG-111, BUG-112, BUG-113, BUG-114, BUG-115.
- 계약: 이번 승격은 새 제품 정책·계약을 추가하지 않는다.
- 승격 수리 기록: BUG-118, BUG-117.
Phase 현황
| Phase | 내용 | 상태 |
|---|---|---|
| Phase 1 | 포함 작업 완료 및 후보 확인 | 완료 |
| Phase 2 | staging full·승격·배포 검증 | 완료 |
| Phase 3 | main 승격·Production 검증·기록 | 완료 — 사후 브라우저 실패는 별도 기록 |
1. 배경
v0.17.9 운영 이후의 데스크톱 로그아웃, 운동 날짜 분리, 종목 요약 배치, 화면 전환 수리가 v0.17.10에 모였다. 사용자는 #1554의 후속 작업이 끝나기를 기다린 뒤 이 변경을 main과 운영까지 반영하도록 승인했다.
2. 문제 제기
개별 작업이 release에 병합된 사실만으로 실제 운영 반영을 판단할 수 없었다. 특히 #1554는 기존 전환 수리 이후 메인 데이터의 공개 시점을 바꾸는 추가 작업이 진행 중이었다. #1546은 이미 완료한 운영 데이터 수리이므로 코드 배포 때 다시 실행할 작업과 구분해야 했다.
3. 해결 방안
2026-09-11 오너 승인 범위에서 최종 후보를 확정하고 기존 release→staging→main 경로를 따른다. 다른 담당자의 작업 PR·브랜치·큐를 조작하지 않고 실제 merge SHA를 확인해 후속 후보 누락을 막는다.
| 방안 | 판단 |
|---|---|
| 후보 전체의 기존 full CI와 환경별 배포·smoke 확인 | 채택. 개별 검증을 합친 최종 코드와 실제 운영 스택을 연결한다. |
| 후속 작업을 빼고 먼저 승격하거나 필수 검증 생략 | 기각. 요청한 포함 범위와 기존 릴리스 정책에 맞지 않는다. |
| #1546 데이터 수리를 다시 실행 | 기각. 승인된 복구와 집계·피드 확인이 이미 끝났으며 이번 배포에는 원인 코드만 필요하다. |
4. 적용한 내용
Phase 1 — 후보와 포함 변경
| 이슈 | 앱 PR / release merge | 사용자에게 달라지는 동작·기록 |
|---|---|---|
| #1530 | #1538, a66c706b | 데스크톱 고정 메뉴의 로그아웃. 작업 기록 |
| #1529, 관련 #1546 | #1539, 4d9705c6 | 일지 탐색 날짜와 운동 저장 날짜 분리. 수리·복구 기록 |
| #1552 | #1555, 82d2073c | 누적 볼륨 숫자와 kg를 영역 안에 함께 배치. 작업 기록 |
| #1554 | #1556, dbf39b1f | 전환·편집 복귀 시 본문과 입력 유지. 작업 기록 |
| #1554 후속 | #1558, 097ae8a9 | 메인 요약·종목·등급 준비 후 함께 공개. 작업 기록 |
5개 PR의 merge SHA와 최신 main f7195e8c, staging 6b678da4가 모두 후보의 조상임을 확인했다. 후보 tree는 cab8ab9475e8e5be77196db5d5cd755101d4a43d다. 앱·검사 41파일, +1,605/-210이며 신규 migration·DB·Edge·workflow·scripts 변경은 없다. v0.18.0 리팩터링을 이번 후보에 섞지 않았다.
최종 release의 npm run check는 정적 검사 및 단위 3,400 통과·0 실패·35 조건부 제외, 단위 실행 100.97초였다. 작업별 필수 precheck와 Merge Check를 확인했으며 마지막 #1558의 Merge Check 34502111685는 성공했다.
Phase 2 — staging 승격
PR #1559의 첫 hosted full 34502849353은 단위 4건·화면 2건·CASE-026·035 실패로 종료했다. DB migration-smoke·정적 검사·나머지 browser shard는 성공했으며 evidence/verify는 앞선 실패를 정상 차단했다.
Node 22에서는 일지 회귀 테스트가 native ESM TypeScript로 읽은 계정 Context와 tsx가 CommonJS로 변환한 앱 shell의 Context가 달랐다. 확장자만 .js로 바꾸어도 4건이 계속 실패했으며, 테스트가 createRequire로 실제 shell과 같은 Context를 읽도록 바꾸자 Node 22.23.2와 Node 24.16.0에서 각각 4/4 통과했다. Context 동일성과 운동 날짜를 변경하지 않는다는 기존 단언을 유지했다. f7bbce97의 Node22 전체 check는 3,400 통과·0 실패·35 조건부 제외, 필수 precheck도 정적·단위 2묶음 통과(1분52초)였다.
화면 보존을 위한 .tab-keep가 app과 screen 사이에 생겼지만 화면 테스트 2곳은 이전 직계 자식 경로를 찾았다. 최상위 탭의 실제 scroll 컨테이너를 가리키도록 선택자 2줄을 수정했다. 고정 7daa7b95에서 Node22 ci:full-local --only viewport는 격리 DB 준비와 실제 Chromium 22/22, 실패·제외·flaky 0으로 통과했다(전체3분58초, 화면 검사2분9초). 부분 재현이며 전체 CI 통과가 아니다.
CASE-026·035는 완료 후 group/home 중 DOM의 첫 항목을 검사해 보존된 hidden home을 골랐다. CASE026의 artifact에는 보드·운동·메모 저장 3회가 모두 HTTP200 finished, browserAndNetworkFailures=[]이고 실패 화면에는 그룹 보드가 표시돼 있었다. 로그의 revision 충돌은 실패 후 예전 revision으로 정리하려다가 발생한 cleanupFailure였다. 저장 본체의 결함으로 단정하거나 앱 저장 로직을 변경하지 않는다. 실제 보이는 복귀 화면을 검사하고 기존 DB 재조회·오류 수집·정리까지 집중 검증한다.
CASE026·035 집중 검증은 동일한 격리 DB와 앱 bundle에서 수정 전2실패 → 수정 후2통과, 22.4초, 실패·제외·flaky 0, requiredEvidence passed/errors=[]였다. 메모·DB revision1→2·행 id·출처·대표 결함 주입·재진입·cleanup을 유지했다. 최종 cee9aca6 Node22 check는 3,400통과·0실패·35조건부제외, 필수 precheck도 static·단위1671+1729통과(1분53초)였다.
PR1560은 Merge Check34505294663 성공 후 01:58:45 KST에 release 6a42781a로 병합됐다. 수리는 테스트5파일+9/-5이며 전체 후보는45파일+1,613/-214, 신규 DB·Edge·migration 변경 없음이다. 최종 tree는 54a4c35dcbf3f9e082b8d8950b5c92acc00fee13이며 두 번째 hosted full34505385373의 실제 검사 잡은 모두 통과했지만, 아래 계정 실행 제한으로 증거·verify 잡이 시작되지 못했다. Merge Check 성공은 full 성공을 뜻하지 않는다.
2026-09-11 오너의 재개 요청 뒤 같은 head/base에서 실패 잡만 재실행했다. attempt2는 e2e-evidence11초·verify21초를 포함해 전체 success로 완료됐다. 이미 성공한 실제 검사 잡은 다시 실행하지 않았다.
11:13:23 KST에 PR1559를 staging 2b8410f4f306c4c00bf5d355a05aed28216e41b7로 병합했다. 검증 tree54a4c35d와 같으며 staging Deploy34553776192의 DB55초·Edge27초·앱67초·smoke26초가 성공했다. 실제 CRUD12/12, 실패·제외0이며 11:17:21 KST 완료했다.
Phase 3 — main과 Production
staging과 같은 후보로 main PR1562를 열었다. main f7195e8c가 staging의 조상이며 신규 코드 tree는 검증·배포한54a4c35d와 같다. 실제 main 검사 commit은7c7b36c36c26c5501b99eae0ea5535353debcde9다. main full34554074904의 모든 필수 잡이 success였고, evidence에서 사용자 여정53개+화면22개·같은 SHA·첫 시도·정리 완료를 확인했다.
11:27:59 KST에 main e49545f15d3454b7e19a0330202139b2a7436fa9로 merge commit 병합했다. staging과 tree54a4c35d가 같고 요청한 작업·수리의 6개 release merge 커밋이 모두 조상이다. Production Deploy34554726839의 DB68초·Edge28초·앱76초·smoke32초가 성공했다. 공개 origin이 이번 배포를 제공함을 확인했고 CRUD12/12, 실패·제외0이었다. v0.17.10 태그·Release가 11:32:18 KST에 생성돼 같은 main을 가리킨다.
별도 Production 브라우저 사후 점검은 1통과·14실패·0제외·0flaky였다. 재시도0, 실제 검사233.05초, 잡4분29초다. CASE-016만 통과했고 실패는001·002·004·005·006·008·010·011·012·013·014·015·029·039다. JSON 결과의 revision은 운영 main e49545f1, run은34554726839로 일치한다.
관측한 실패는 사용자 종목 저장 개수0(CASE001), UI 클릭·가시성 대기(002·004·005·008), 통계 projection superseded(006), 사전 chunk 요청0(010), 빈 초안 보관 단언(011), 수집된 여정 오류(012~015), 로컬 service-role 요구와 관측기 설치 전 종료 후 cleanup 오류(029·039)다. 이번 승격에서 각각의 근본 원인과 실제 사용자 영향을 확정하지 않았으며 모두 제품 회귀 또는 모두 검사 환경 문제로 단정하지 않는다. 원격 결과 목록을 확인했고 기존 #1485 정책대로 배포·태그 성공과 독립된 실패로 기록했다. 추가 수리·별도 이슈·재실행으로 범위를 확대하지 않았다.
운영 반영을 확인한 #1530·1529·1552·1554는 [v0.17.10 반영완료]를 붙여 닫았다. #1546은 기존 완료 상태를 유지했다. 릴리스 이슈 #1557에는 두 승격·배포·태그와 사후 검사 결과를 연결한다.
main에서는 full34554074904가 다시 실행됐다. 오너가 중복 검사 여부를 질문해 성공 기록과 실제 판정을 조회했다. tree54a4c35d의 check103121794774는 success였지만 API의 details_url은 /runs/103121794774, 저장된 runUrl은 /actions/runs/34505385373으로 달라 findValidatedTree의 동일성 조건에서 제외된다. 원 실행의 pull_requests도 빈 배열이라 validRun의 PR 연결 조건을 통과하지 못한다. staging 배포 증거는 정상 인정됐으며, 코드 차이나 staging 실패로 재실행된 것은 아니다. 재사용 정책과 실제 코드 판정의 차이로 기록하고 이번 질문을 별도 구현·이슈의 승인으로 확대하지 않았다.
재개 전 GitHub Actions 실행 제한
두 번째 full34505385373에서는 scope·정적·단위2묶음·migration-smoke·브라우저4묶음·화면 검사가 모두 success였다. 그러나 e2e-evidence와 verify는 runner와 step 없이 시작 단계에서 실패했다. 증거 잡과 verify 잡의 annotation은 최근 계정 결제 실패 또는 사용 한도 증가 필요로 잡을 시작하지 않았다고 명시한다. 이 안내만으로 두 원인 중 어느 쪽인지는 확정할 수 없다.
원격 artifact를 내려받아 저장소의 동일한 check-e2e-evidence.mjs를 Node22로 실행했다. 실제 검증 merge8846791904aa3b2574b91eabb61a305d342b3110과 run34505385373을 지정했고, 사용자 여정53개+화면22개가 같은 SHA·첫 시도·정리 완료라는 결과로 통과했다. 이 로컬 증거 확인은 GitHub verify 실행·성공을 대신하지 않으며 full workflow 자체는 failure 상태다.
오너가 다시 실행하도록 요청한 뒤 같은 후보에서 실패 잡만 재개해 원격 검사 성공을 확인했다. 실제 계정의 결제·한도 설정 변경 내용은 조회하지 않았다. 보호 규칙 우회, 성공 상태 수동 등록, 실행 정책 변경은 하지 않았다.
작업 중 드러난 것
최초 확인에서 #1554의 최신 이슈 댓글이 Precheck를 가리켰지만 실제 #1558은 01:27:24 KST에 병합돼 있었다. 사용자 요청에 따라 merged PR과 원격 release를 다시 확인해 후보에 포함했다. 이슈 댓글·세션 상태를 실제 Git 병합 완료의 대체 증거로 쓰지 않는다.
사전 검증 누락: 최초 최종 후보 검사를 시스템 Node24로 실행해 .nvmrc Node22의 로더 차이를 놓쳤다. 또한 #1554의 보존된 화면 구조를 기존 전체 화면 검사·두 CASE 선택자가 처리하지 못했다. 검사 단언·대상·timeout·retry·필수 오류 수집·cleanup을 제거하지 않고 로딩·대상 선택만 수정한다.
5. 적용 결과
| 항목 | 전 → 후 / 검증 범위 |
|---|---|
| 최종 후보 | 후속 #1554 검사 중 → 5개 앱 PR 포함 확인 |
| 코드 기본 검사 | 최종 후보 미검증 → 3,400 통과·0 실패·35 조건부 제외 |
| 신규 DB/Edge 변경 | 없음 → 없음 |
| #1546 데이터 복구 | 운영 복구 완료 → 재실행 없음 |
| 원격 실제 검사 | 첫 실행 단위4·화면2·CASE2 실패 → 수리 후 모든 검사 잡 success |
| 증거 검사 | 원격 잡 미실행 → 같은 artifact의 로컬 검사53+22 통과 → 재개한 원격 evidence·verify success |
| staging | 실행 제한 대기 → full·병합·DB·Edge·앱·CRUD12/12 성공 |
| main | full 성공 → PR1562 / e49545f1 병합, staging과 tree 일치 |
| Production | DB·Edge·앱·공개 origin·CRUD12/12 성공, v0.17.10 태그 생성 |
| 별도 운영 브라우저 사후 검사 | 1통과·14실패·0제외·0flaky. 위 성공과 별도이며 사용자 영향·근본 원인은 미확정 |
6. 이번 개선으로 향상된 것
요청한 개별 수리와 필요한 테스트 정합 수리를 같은 release 후보에 모아 main과 Production에 반영했다. 실행 제한으로 대기했던 필수 잡을 재개해 성공했고, 같은 코드의 두 환경 배포·실제 CRUD12/12 및 운영 태그까지 연결했다.
검증 범위와 제한
요청한 main 승격·운영 배포·태그와 결과 기록을 완료했다. 운영 브라우저 사후 검사의 14실패를 성공으로 보고하지 않는다. CI 재사용 판정 차이와 사후 점검의 추가 수리는 이번 릴리스 요청에 임의로 붙이지 않았다. 오너 손이 필요한 것: 없음.