같은 tree의 전체 검증 결과 재사용 (2026-09-09)
- 기간: 2026-09-09~10, Codex 1세션. 오너 지시: “바벨릭 #1476 진행해줘 17.7 릴리스 포함”.
- 릴리스: v0.17.7 release 통합·필수 full CI 성공 확인. 통합 PR #1484, merge
e56604cb0ae4d9fcaa361fbd99c3978dabb60535, 검증 tree12d339cd21d807000671f80e69de477e4d592d64. main 큐 활성화·Production 완료는 아직 확인하지 않았다. - 이슈: Barbelic #1476.
- 원 작업: 앱 PR #1482,
fix/1476-validated-trees. Phase 16dcc6f3d· Phase 2e9ef9a25· Phase 3c47555e2· Phase 5ddeeca65. 변경은 HQ 통합 PR #1484에 포함됐으며 원 PR은 최종 main/Production 신호 전까지 유지한다. - HQ 통합용 최신 main 적용본: base
df394f77, 커밋5a25b311→d5f9031c→3f661255; head3f661255ee9a80fcc0a27a391574b31fcf3e384d, treee282e66d5a4072ac6c35122db64a0010886918ad. 원래 PR·head는 유지한다. - HQ의 실제 후보
526cda1710241e9c578e60982faf648720cb7fda와 조정한 최종 전달본: merge commit84b99360f52947206b5090752693f10cff136d96, tree346d95cb9341971c4c66ff5949b1144ffdb89a6d. 부모는 HQ 후보와 위 main 적용본이다. - 설계: 이슈 분석·계획.
- 정본: 릴리스 프로세스, 릴리스 큐, 로컬 검증.
- 도구: 앱의
scripts/ci/validated-trees.mjs,scripts/ci/validation-base-checks.mjs,scripts/check-promotion.mjs,scripts/migrations/release-landing-service.mjs. - 게이트: 검증 장부·임시 Git 저장소의 실제 병합·승격 검사·실제 verify shell 동작 테스트. 마이그레이션·제품 함수·제품 기능은 변경하지 않았다. 사전 검증에서 확인된 기준 스키마 스냅샷과 파생 정의의 불일치는 Phase 5에서 보정했다.
- 버그리포트: BUG-088.
Phase 현황
| Phase | 내용 | 상태 |
|---|---|---|
| Phase 1 | 성공 tree 기록·조회와 hosted/큐 기록 연결 | release 통합·필수 full CI 통과 |
| Phase 2 | 큐의 동일 tree 재사용 | release 통합·필수 full CI 통과, 원격 재사용 실측은 미완료 |
| Phase 3 | 승격 scope와 verify의 재사용 | release 통합·필수 full CI 통과, main 활성화는 별도 |
| Phase 4 | 운영 문서·작업 기록·문서 검사 | 로컬 작성·검증 완료 |
| Phase 5 | 기존 기준 스냅샷과 깨끗한 DB 재생 결과 일치 | 로컬 보정·검증 완료 |
1. 배경
작업 브랜치에서 전체 검증을 통과해도 큐·staging 승격·main 릴리스에서 다시 전체 검증을 실행했다. 커밋 번호는 병합 과정에서 바뀌지만 실제 파일 내용은 같은 경우가 있다. #1476은 파일 내용의 Git tree hash를 기준으로 이 중복을 제거한다.
2. 문제 제기
큐는 후보 merge commit 생성 뒤 전체 검증을 항상 호출했다. 승격 스크립트도 경로·배포 증거 확인 뒤 항상 requires_full=true를 출력했다. 검사한 내용과 실행 출처를 다시 조회할 장부가 없었다.
이슈 본문의 “4회 약 61분”은 이슈 작성자의 v0.17.6 실행 기록이다. 이번 세션이 다시 측정한 수치가 아니며, 개선 후 시간과 직접 비교한 실측으로 사용하지 않는다.
3. 해결 방안
오너는 2026-09-09 진행 지시로 이슈 범위와 v0.17.7 편입을 승인했다.
| 안 | 판단 |
|---|---|
| 전체 검증에 성공한 정확한 tree와 실행 출처를 저장하고 재사용 | 채택. 커밋 부모·메시지가 달라도 실제 파일 내용이 같은지 확인한다. |
| 최근 초록 실행·브랜치 이름·skip 라벨만 보고 생략 | 기각. 최종 병합 내용이 실제 검증 내용과 같은지 보장하지 못한다. |
장부는 저장소의 공통 초기 커밋에 full-ci-tree:<tree> check-run으로 저장한다. 기록에는 tree·검사한 commit·base·실행 링크·실행 회차·워크플로 경로와 SHA가 포함된다. GitHub Actions가 발급한 성공 기록과 해당 저장소의 실제 성공 실행만 인정한다. 조회 실패·누락·다른 tree·잘못된 출처는 전체 검증으로 돌아간다.
4. 적용한 내용
Phase 1 — 성공한 전체 검증 기록
hosted verify가 모든 필수 단계 성공을 확인한 뒤 실제 체크아웃의 tree를 기록한다. 큐도 전체 검사 성공과 검사 후 작업 트리 일치를 확인한 뒤 기록한다. 실패·취소·진행 중인 원본 실행은 재사용하지 않는다. 장부 기록 실패는 통과한 전체 검증을 실패로 바꾸지 않으며, 다음 실행은 기록이 없으면 전체 검증을 수행한다.
Phase 2 — 큐의 동일 tree 재사용
후보 생성 뒤 성공 기록이 있으면 전체 검사 호출을 건너뛴다. 비교 base가 달라질 수 있으므로 설치된 불변성 검사·마이그레이션 순서·위험 검사는 유지한다. 앱·문서 분리로 앱에서 제거된 문서 검사는 다시 도입하지 않는다. 최종 PR head/base·라벨·clean 상태·commit/tree·조건부 push 확인은 공통 경로로 계속 실행한다.
재사용 결과에는 full:false, reused:true, 원본 실행 링크를 남긴다. 재사용을 새로운 전체 검증 성공으로 다시 기록하지 않는다. --dry-run은 장부를 조회하지 않고 항상 전체 검증한다.
Phase 3 — 승격과 최종 판정
승격 경로·PR SHA와 부모 관계를 먼저 확인한다. main 승격에서는 현재 staging 배포 성공과 병합 tree가 staging tree와 같은지도 확인한다. 이 배포 증거는 장부로 대체하지 않는다.
그다음 PR merge tree의 성공 기록이 있으면 full 검사 단계들을 건너뛴다. verify는 scope 성공, tree/원본 실행 링크, 모든 full 단계의 의도된 skip을 확인해야 성공한다. 새 tree와 수동 full 실행은 기존 전체 검증을 수행한다.
Phase 4 — 문서 분리와 적용 상태
운영 규칙·보고서는 dekerd/Barbelic-docs에서 관리한다. 앱에는 기록용 문서 변경을 만들지 않았다. 큐 제어 코드는 main의 workflow에서 실행되므로 release에 코드가 합쳐진 것과 새 큐 기능이 활성화된 것은 구분한다.
작업 중 드러난 것
Phase 3 최초 로컬 검사에서 기존 테스트의 “docs 빌드는 항상 success”라는 단언 1개가 재사용 동작과 충돌했다. full일 때 success, 재사용일 때 skipped를 요구하도록 수정하고 전체 검사를 다시 통과했다. 원격 CI를 우회하거나 성공 상태를 수동으로 만들지 않았다.
사전 검증에서는 release 기준의 supabase/schema.sql에 마이그레이션이 생성하지 않는 dblink extension이 들어 있어, 깨끗한 DB 재생과 스냅샷 비교가 실패했다. 해당 스냅샷·마이그레이션·생성기는 #1476 작업 전후가 같았다. 과거 생성 명령 순서는 확인하지 않았다. 다음 full을 직접 막는 확인된 불일치이므로 이 작업의 격리 DB에서 공식 생성기로 스냅샷을 재생성하고 diff·비교를 확인하는 Phase 5만 추가했다. 제품 SQL·운영 DB 수리나 별도 구현 이슈로 확대하지 않는다.
Phase 5의 첫 npm run check에서는 스냅샷만 갱신한 상태여서 SQL 정의·후보 생성 테스트 14개가 파생 정의와의 불일치를 검출했다. 공식 sql:extract로 extension 정의와 registry를 함께 갱신했다. 최종 차이는 dblink 선언 제거 및 개수·해시 갱신이며, migration·함수·권한·예외 목록 변경은 없다. sql:check와 격리 DB의 스냅샷 비교는 통과했다. 이 작업의 로컬 DB 스택만 종료했다.
5. 적용 결과
| 항목 | 전 → 후 또는 확인 결과 |
|---|---|
| 성공 tree 장부 | 없음 → 기록·조회·실행 출처 검증 구현 |
| 큐 full 호출 | 항상 1회 → 동일 tree 테스트에서 0회, miss·API 오류·dry-run에서 1회 |
| release 변경·base 검사 실패 | 재사용 경로에서도 push 차단 확인 |
| 승격 배포 검사 | 재사용 시에도 staging 실패·다른 tree·오래된 PR 차단 확인 |
Phase 1 npm run check | 3,406 성공 · DB 조건부 27 skip · 실패 0; 장부 별도 55/55 |
Phase 2 npm run check | 3,469 성공 · DB 조건부 27 skip · 실패 0; 큐·base 별도 26/26 |
Phase 3 npm run check | 3,475 성공 · DB 조건부 27 skip · 실패 0; 승격/verify 28/28 · 도구 계약 7/7 |
Phase 5 npm run check | 3,475 성공 · DB 조건부 27 skip · 실패 0; snapshot --check와 sql:check 통과 |
| 최신 main 통합 준비 | Node 22.23.2에서 충돌 조정에 관련된 명령 등록·workflow 권한·verify 판정·큐 격리 env/기본 호출 9/9 통과, 6.13초. 전체 check/full 및 원래 장부 계약 묶음은 반복하지 않음. |
| HQ 실제 후보 통합 | 문서 의존성을 복원하지 않은 기본 큐 full·동일 tree 재사용·verify/권한 연결부 8/8 통과(11.18초), 실제 후보의 고정 base migration 순서/위험 검사 통과. 시간 제한·새 명령·표시 이름은 HQ 버전 보존. |
| 원격 장부 조회 API | 공통 초기 커밋에서 check-name 조회 성공, 기존 기록 0개 |
| 문서 검증 | 초기 내비게이션 295개·자산 31개 확인. 2026-09-10 최신 docs main 통합 후 npm run check · build:docs · check:docs-artifact 통과, 내비게이션 300개·자산 31개, 빌드 20.97초 |
| 최신 release 사전 검증 | ci:precheck-local이 없는 현재 checkout에서 기존 ci:local --only verify,db 실행. verify 8단계·DB 재생·pgTAP 129파일/2,322 assert·동시 저장 13건·통계 수렴 통과. 스냅샷 비교 1개 실패는 Phase 5 보정 후 같은 격리 DB에서 재검사해 통과했다. 전체 CI 실행·통과로 보고하지 않는다. |
| 문서 원격 CI | 실행 34354365766 docs·verify 성공, admin은 문서 변경 범위에 따라 skip |
| 초기 개별 hosted full | 34356019815 실패, 생성~종료 14분 59초. scope·static·unit·docs와 migration 재생·스냅샷·DB 계약·동시 저장·통계 수렴 통과. empty-account·browser shard 1/2/4·viewport 실패, 최종 verify/evidence도 실패. viewport는 2 failed / 18 passed. |
| 초기 개별 release 큐 | 34356131787 실패, 잡 1분 54초. 새 main controller의 ci:full-local 명령이 옛 release 기반 후보에 없어 full 본체 전 종료. 병합 없음. |
| 실제 장부 기록·재사용 | head ddeeca65, tree 01ad0ae64aaa480876bf8771d57b602a192b21fe 조회 결과 receipt:null. 실패한 full을 성공으로 기록하지 않았으며 실제 재사용 시간은 미검증. |
| 최종 HQ 통합 full | 34372922344 성공. full 본체 7분 32초, 브라우저 56건·화면 22건 모두 failed/skipped/flaky 0. static·unit·migration 재생·스냅샷·pgTAP 130파일/2,328 assert·동시 저장·통계 수렴·empty/cardio/persistence·evidence 통과. 조건부 단위 skip은 브라우저/화면 수치와 구분한다. |
| release·staging 코드 통합 | PR #1484로 release e56604cb, PR #1488로 staging 213aaeb7c21598e3023cf47d16ec756feabad80a. staging 배포 34374171430는 HQ 공유 시 진행 중이었으며 성공으로 기록하지 않는다. |
| main·Production 반영 | 아직 완료 확인 없음. HQ의 최종 릴리스·배포 결과와 구분 |
6. 이번 개선으로 향상된 것
같은 소스를 검증했다는 근거를 다음 병합 단계에서 찾을 수 있게 했다. 전체 검사 생략 여부는 브랜치 이름이나 담당자의 판단이 아니라 tree 동일성과 성공 실행 증거로 결정한다. 실행 로그와 결과에는 재사용한 원본 링크가 남아 실행한 검사와 재사용한 검사를 구분할 수 있다.
남은 것
코드는 HQ 통합 PR #1484로 release에 반영됐으며 필수 full CI도 통과했다. 원 PR #1482의 별도 큐는 다시 실행하지 않았고 최종 main/Production 신호 전까지 유지한다. main 큐 활성화와 원격 재사용 시간 실측은 아직 미완료다. 최종 release→staging→main 승격과 전체 릴리스 기록은 HQ 담당이다. 통합 full의 7분 32초는 병렬화 등 다른 작업을 포함한 실행 시간이며 #1476의 재사용 단축 실측값이 아니다.
초기 원격 실패는 빈 계정의 get_pr_overview 스냅샷 준비 부족(55000, generation 0, as_of 2026-09-07), 통계 cleanup drain·remaining_count=1 및 화면 동작 종료 등이었다. 당시 최신 main의 fixture 준비·postgres 역할 drain·hosted 샌드박스 전달 수리를 포함한 HQ 통합 후보에서 위 최종 full 성공을 확인했다. 이 세션이 초기 실패를 별도 로컬 재현하거나 사전 검증에서 검출한 것으로 보고하지 않는다.
HQ는 작업별 full 반복을 줄이기 위해 통합 후보를 검증했다. 이를 위해 최신 main 위의 별도 작업트리에서 #1476 적용본을 만들었다. 삭제된 landing-queue.yml을 복구하지 않고 새 release-merge-request.yml에 장부 권한을 옮겼으며, main의 요청 CLI·큐 환경·ci:full-local·결과 요약을 보존했다. Phase 5 스냅샷 보정은 main에 이미 있어 다시 적용하지 않았다. 최종 차이는 workflow/CI 코드/테스트 13개 파일이며 제품·migration·schema 차이는 없다. 이 산출물은 HQ에 전달한 로컬 커밋과 bundle이며 원격 release 반영 증거가 아니다.
이후 HQ 실제 후보와 병합하면서 policy-contract·release-landing-service·ciLocal/ciToolchain 테스트의 충돌 4곳을 조정했다. 제거된 docs-build·DOCS_RESULT·evidence.docs 검사를 다시 넣지 않았고, HQ 기준 최종 차이는 자기 CI/테스트 12개 파일이다. 통합 커밋과 bundle은 HQ에 전달했으며 원래 작업 PR·브랜치는 유지했다.
기존 main controller가 장부 기능 없이 마친 큐 성공을 나중에 장부에 옮기는 경로는 현재 구현에 없다. 기록은 #1476 controller의 full 성공/clean 후보 검사 직후 또는 hosted verify의 전체 성공 뒤에만 실행되며, record-hosted CLI는 해당 hosted 실행의 실제 checkout과 GITHUB_SHA가 같아야 한다. 기존 큐 로그의 candidate/tree/실행 링크는 보고 근거로 보존할 수 있지만 자동 재사용 장부를 발급하지 않는다. 별도 우회 기능이나 성공 기록은 만들지 않았다.