Skip to content

Phase 1 인계 결함 보완 — 저장 원문·통계 기준·DB 합격 판정을 실제 경로로 검증 (2026-09-07)

  • 기간: 2026-09-07. 오너 지시: “Pr 1318 머지됐다니까 확인해주고, phase 1 잔여작업은 너가 좀 진행해줘”. HQ 통합과 S01·D01·D13의 독립 보완을 한 브랜치에서 진행했다.
  • 랜딩: 기준 main c2bf7f605(#1318·#1321 포함). 보완 PR #1322bf9064973d250043351b148336ebc7347f5d6446 (2026-09-07T07:55:20Z). 전체 CI, 자동 랜딩, staging Deploy 성공. 신규 migration·DB 객체·Edge·제품 UI 변경은 없다.
  • 설계서: 없음(Phase 1 종료 점검의 수리). 62개 계획 ID·직접 의존·Phase 순서는 유지한다.
  • 정본: 저장 codec, 통계 DAG, populated upgrade, 전체 로드맵.
  • 도구: 기존 로컬 Supabase sandbox·CASE-039·D01 oracle/workload·D13 harness. 새 실제 mirror/IndexedDB 검사는 npm run test:persistence-browser이며 기존 full CI 마지막 브라우저 shard에서 한 번 실행한다. ci:localpersistence 단계에도 같은 명령을 연결했다.
  • 게이트: check/build·unused·문서 빌드, 실제 storage 왕복, 고정 입력 통계 재적재, 구·신 탭 공존, 위험 증거의 실제 거부. 로컬 preflight: 109파일·1,899 assert 통과(2026-09-07T07:26:35.746Z), CASE-001–039 중 활성 38개 및 viewport 14개 통과. 감사 manifest는 수정하지 않는다.
  • 계약: S01 준비 결과를 prepared / needs_preparation / invalid로 명시하고 D13 실측 증거를 v2로 갱신한다. 기존 사용자 요청 identity를 다시 만들거나 과거 migration을 수정하지 않는다.

1. 배경

Phase 1은 공통 계약과 다음 세션이 사용할 구현·검증 기반을 만드는 단계다. 종료 점검 시 10/11 PR이 병합돼 있었고, 이후 #1318과 D01 후속 #1321까지 병합됐다. #1318의 후속 세션은 Supabase 4년 실측·중단 재개·전체 CI·스테이징도 실행했다. 최초 점검의 “지원 환경 미실행”과 장부 미분류 1개는 이 후속 변경에서 해소됐다.

그러나 개별 함수의 테스트가 통과해도 실제 storage 복구나 두 API를 연결하는 경로, 실패한 측정 결과의 판정에는 결함이 있었다. 총괄 문서도 G01/G05만 완료로 적어 실제 PR 진행에 뒤처졌다.

2. 문제 제기

원문 보존이 codec 앞의 저장소 필터에서 끊겼다

미러의 비UUID/누락 키 행을 codec 이전에 버렸다. 빈 IndexedDB를 복구하고 정상 행을 갱신하면 미러가 다시 쓰이면서 해석 불가 원문이 사라졌다. withAttempt가 제거한 메타데이터도 다음 보존 병합에서 되살아났다. 구형 계획의 없는 hash를 빈 문자열로 캐스팅해 유효한 PreparedMutation으로 반환하는 경로도 있었다.

통계 기준이 실행일·적재마다 달라졌다

worker가 실행일로 만든 3일 snapshot 창이 고정 SHA 비교에 들어갔다. 실제 DB 재실행에서는 날짜 외에도 UUID 치환 뒤 행 순서, session_created_at/source_created_at, UUID로 결정하는 동률 대표 세트가 달라 9개 표의 SHA가 어긋났다. #1321에서 catalog_version을 제외한 뒤 기존 baseline SHA도 갱신되지 않았다. 날짜·참조 열을 빼거나 SHA만 다시 쓰면 동률·출처 의미의 회귀를 놓친다.

부적합한 DB 실험도 합격으로 표시됐다

D13은 빈 fixture·실제 probe 오류·시간/잠금 예산 초과를 합격 판단에 반영하지 않았다. 기본 probe의 JWT에는 session.user_id 대신 session.id가 들어갔다. 실제 소유자의 기록을 읽지 않아도 오류 0으로 보일 수 있었다.

공존 검사가 진행 중 조회를 새로고침으로 끊었다

실패 CI trace에서 저장 후 get_calendar_day_summary가 시작되고 323ms 뒤 reload가 실행됐다. waitForLoadState('networkidle')는 앞서 충족된 lifecycle 상태를 즉시 반환했다. 앱 오류를 무시할 문제가 아니라 테스트의 현재 요청 완료 조건이 빠진 문제였다.

3. 해결 방안

  • D1: 오너가 승인한 Phase 1 잔여 수리 범위에서 진행한다. 새 HQ 상주 승인·예산 완화·계획 ID는 추가하지 않는다.
  • D2: 원문·유효 identity·날짜·동률 정책을 보존한다. 코드가 모르는 값을 성공 상태로 추측하지 않는다.
  • D3: 통계 테스트의 입력만 격리 sandbox에서 고정하고 운영 SQL·쓰기 엔진·계산식은 유지한다. 이미 적용된 migration도 변경하지 않는다.

4. 적용한 내용

  • S01: 저장소는 원문 unknown[]를 반환하고 codec이 유효 v1과 보존 원문을 분류한다. 미러를 다시 쓸 때 보존 집합을 합쳐 증식·유실·정상 삭제 후 부활을 막는다. 메타데이터 삭제 의도는 최종 병합까지 유지한다. 구형 계획 identity가 아직 없으면 원문과 누락 필드를 가진 needs_preparation으로 S03에 넘긴다.
  • D01: 고정 anchorDate를 기존 snapshot 발행 RPC까지 전달하고 실제 3일 창을 검사한다. UUID를 논리 이름으로 바꾼 뒤 논리 키로 다시 정렬한다. disposable workload의 UUID 상대 순서와 생성시각만 고정하는 한정된 fixture를 추가했다. 명시 sandbox와 컨테이너가 일치해야 하고 아직 없는 owner·등록된 source_ref에만 작동한다. 임시 함수·고유 trigger는 finally 제거하며 다른 owner의 입력을 바꾸지 않는 DB 검사를 둔다. 수동 worker 검증 중에는 통계 cron 3개만 격리하고 원래 active 상태를 finally 복구한다. 기존 실행이 끝난 뒤 시작하며, 같은 sandbox의 중복 D01 실행은 거부한다.
  • D13: evidence.mjs의 공통 측정 판정이 harness와 high-risk landing 증거에 같은 조건을 적용한다. 실제 fixture 행·소유자 세션·각 migration의 owner probe·오류 0·파일 60초/잠금 5초가 필요하다. sandbox는 모든 prepare 모드에서 정본 Postgres 이미지인지 확인하고 임시 workdir에도 같은 버전 핀을 쓴다. v2 증거는 사실 보존과 전체 verdict를 별도로 기록한다. 기본 probe는 실제 사용자와 RLS 기록을 확인한다.
  • CASE-039: 기존 observeRpcDrain을 재사용해 해당 탭의 현재 조회·통계 갱신 완료를 확인한 뒤 닫기/reload한다. 오류 모니터와 flaky 금지는 그대로 둔다.
  • HQ: 11개 원 PR의 상태·SHA·개별 updates를 총괄 문서에 연결하고 보완 경로를 동기화한다. 새 회귀 검사 파일은 커버리지 장부에 분류한다.

작업 중 드러난 것

처음에는 D01 날짜만 고정했지만 실제 1년/4년 적재에서 9개 SHA가 달라졌다. 원인은 입력의 시각·UUID·정렬이었고, 관측 열을 제외하는 대신 고정 입력으로 보완했다. 기준 해시는 실제 DB에서 다시 생성하고, 다른 disposable owner의 별도 재적재에서 같은지 검증한다. 이전 baseline의 전체 원문은 없어 옛 수치를 전부 직접 대조했다고 주장하지 않는다. 기존 SQL 유지·독립 oracle·불변식·동일 입력의 재실행 일치가 근거다.

4년 적재가 정각을 지나며 1,043건 중 한 건이 Completed session write must enqueue exactly one statistics generation으로 거절됐다. 2026-09-07 07:00:00 UTC에 backfill-scan·stats-refresh·overview-rollover cron이 실행된 것을 확인했다. 별도 2연결 재현에서도 fixture trigger 없이 같은 실패가 발생했다: 첫 저장으로 N 생성 → A가 owner enqueue advisory lock 보유 → B의 둘째 저장이 prior=N을 읽고 enqueue 대기 → A가 별도 enqueue(N+1) 후 commit → B의 enqueue(N+2)가 +1 단언에 걸려 rollback. 첫 저장 1개는 보존됐다. 이는 원 계획 **D02(P0)**의 writer 세대 원자화가 해결할 실제 결함이며, D01에서는 cron을 격리해 결정성 입력으로부터 분리한다. 저장 실패를 성공으로 세거나 제품 SQL을 임의로 바꾸지 않았다.

Supabase의 postgres 역할은 cron.job 직접 UPDATE 권한이 없어 최초 격리 구현의 실제 실행이 실패했다. cron.alter_job의 active 인자를 사용하는 공식 변경 경로로 보완했다. 권한 실패 실행에서는 7개 cron이 모두 원래 활성 상태임을 확인했다.

최종 ci:local의 이미지 검사가 이전 D13 실험 뒤 컨테이너가 17.6.1.158로 바뀐 사실을 발견했다. 원인은 임시 from workdir에 .temp/postgres-version을 전달하지 않은 것으로, CLI가 reset 과정에서 기본 이미지로 재시작했다. 임시 핀·모든 sandbox 모드의 진입/재생 후 실제 이미지 검사·finally 정리를 보완했다. 앞선 5,171ms 실험은 이 다른 빌드의 예비 결과이며 Production과 같은 환경의 증거로 사용하지 않는다. 아래 표는 재기동한 17.6.1.127의 최종 실측이다.

5. 적용 결과

검증결과
원 PR 반영Phase 1 11/11 병합. #1318 e970dd7d, #1321 c2bf7f60 스테이징 성공
S01 집중 검사33/33, 실제 Chrome mirror→IDB→mirror 1/1. 반복 갱신·원문 중복 개수·정상 삭제 후 부활 방지 확인
D13 집중 검사빈 fixture·잘못된 owner·미실행/오류·예산 초과·버전 핀 불일치의 거부 회귀 통과(최종 check에 포함)
D01 초기 고정 날짜oracle·동치·불변식·다른 기준일 거부 통과. 적재 간 SHA 불일치를 추가 발견해 입력 고정까지 보완
로컬 DB replay / snapshot / pgTAP174개 replay·snapshot 일치·109파일/1,899 assert 성공
수정 D13의 실제 4년 실험1,043세션·12,516세트·원본 33,127행. 사실 digest·ID·관계 보존, owner RLS probe 실행, 오류 0/35, 공존 break 0, 원천 일치, 중단 5/11 뒤 rollback·재실행 성공
같은 실험의 예산 판정실패가 맞음: 잠금 5,017ms > 5,000ms. high migration 적용 5,357ms, 전체 5,615ms, WAL 22.26MB. facts=preserved verdict=failed로 기록하고 landing 수락을 거부. 17ms 초과는 큰 성능 악화의 증거라기보다 예산 여유를 입증하지 못한 경계 결과
최종 전체 check2,892개 중 2,878 pass·0 fail·14 DB skip. DB skip은 아래 별도 실 DB 결과로 구분
로컬 full 통합replay·snapshot·pgTAP 및 CRUD 11/11, 빈 계정 7/7, 유산소 6/6, persistence 1/1, 번호별 browser 38/38, viewport 14/14. 13분 12초, flaky 0. 별도 CASE-039 repeat 2회도 retries=0으로 모두 통과
앱·문서·정적 검사build·unused·docs build(관리자 포함)·actionlint·manifest·coverage strict 통과. 미분류 0·미등록 진입점 0
최종 고정 이미지 DB 재검증15/15 pass·0 skip. D01 1년 260건·4년 1,043건을 새 owner로 재적재해 15개 비교 표의 SHA 일치, oracle·재계산 동치·불변식 통과. 다른 기준일(2027-10-12) 창 거부와 고정일 복원도 확인. barrier/backfill/upgrade 도구 9개 포함. fixture trigger 제거와 cron 원래 상태 복구 확인
원격 CI·merge·stagingPR #1322 전체 CI 성공 → bf9064973 머지 → staging DB·functions·frontend·smoke 성공. 위 실제 run 링크 참조. Production release-tag 단계는 skipped
10년 결정성 추가 검증2,609세션·31,308세트, 15표의 full 재계산 동치·정책 불변식·oracle 5/5 통과(약 277초). 실제 전체 snapshot에서 history-10y.json 기준 SHA를 등록한 뒤 별도 새 owner로 재적재해 15표 SHA 비교까지 5/5 통과(약 239초). 입력 코드 출처 aaf5f318c. 이제 1/4/10년 기준 파일 모두 실제 DB 재현 증거가 있음
Production / 실기기이 보완에서 배포·검증하지 않음

실측의 canonical 입력은 합성 workload다. 사용자 운영 DB에 접속하거나 실제 사용자 데이터를 변경하지 않았다. 이번 보완에 신규 migration이 없으므로 기존 v0.17.1 백필을 소급 수정하는 작업도 포함하지 않는다.

최종 고정 이미지 실측의 재현 입력

  • 도구 코드: a6879eba3, 입력 ref: v0.17.0 → 해당 worktree의 변경 없는 migration 174개. Postgres 이미지: public.ecr.aws/supabase/postgres:17.6.1.127.
  • db:upgrade -- --from v0.17.0 --to worktree --sandbox <전용 sandbox> --workload <history-4y> --interrupt 20260913000000 --empty-replay --check-definitions --out <증거 폴더>
  • 빈 replay 174개·98,451ms·snapshot 일치. 사실 digest는 23표 전후 42c93caeddbc로 동일하고 중단 5/11 뒤에도 동일하다. 측정 예산을 넓히거나 실패를 합격할 때까지 반복하지 않았다.
text
-- upgrade-evidence: v=2 env=supabase-sandbox fixture=history-4y populated=true fact-rows=33127 owner-sessions=1043 from=478ddf12 to=a6879eba migrations=2 elapsed=5615ms max-elapsed=5357ms lock-wait=1/5017ms wal=22.26MB probes=0/35 owner-probes=28 unprobed=0 coexistence=0 interrupt=20260913000000:5/11 facts=preserved verdict=failed at=2026-09-07T07:37:07.329Z

6. 이번 개선으로 향상된 것

codec과 저장소가 보존할 원문을 끝까지 전달하고, 다음 세션은 미준비 identity를 명시적으로 처리할 수 있다. 통계 검증은 실행 환경의 시각·UUID 대신 고정 입력을 비교한다. DB 검증기는 실제 오류·예산 초과를 다음 단계에서 사용할 수 없는 증거로 표시한다. 개별 단위 검사와 실제 브라우저/DB 경로의 간격을 줄였다.

남은 것

  • 실제 릴리스 예산: 이미 staging에 적용된 v0.17.1 백필의 4년 측정은 5,017ms로 5초 잠금 예산의 경계에 걸렸다. 고정 이미지의 대표 fixture에서도 충분한 여유를 입증하지 못했다. #1303/R05에서 rollout 조건·구현 개선을 해결해야 한다. Phase 1 검증 도구의 완성과 해당 migration의 Production 승인은 별도다. 이 보완은 예산을 넓히거나 실패를 합격으로 바꾸지 않는다.
  • D02의 확정 재현: 별도 enqueue와 동시에 저장하면 세대 +1 단언으로 실패한다. 위 2연결 재현을 writer 원자화의 첫 회귀 검사로 옮기고, 성공 영수증은 자기 mutation이 배정받은 세대를 사용하도록 해결한다.
  • 계약과 구현의 구분: Phase 1은 최종 작성자 소유권과 full/incremental 동치 판정의 계약·검증 도구를 완성했다. 현재 중복 작성자 제거와 새 incremental 구현의 실제 비교는 D04–D11에서 진행한다. 완료 체크가 이 후속 구현까지 끝났다는 뜻은 아니다.
  • 계획된 후속: S02/S03/S04의 atomic outbox·builder·claim, D02/D04–D11의 실제 writer·worker·generation, U02/U03/U07의 전체 화면·CSS 이식, R02/R05/R06의 전체 호환·리허설·운영 검증은 원 로드맵대로 진행한다.