함정 카탈로그
coui 를 오래된 버전에서 최신까지 올린 실제 이관에서 반복해 겪은 것들이다. 증상이 "화면이 조금 달라졌다"로만 드러나는 것이 많아서, 탐지 방법을 같이 적는다. 버전 번호가 붙은 항목은 그 버전에서 확인한 것이다 — 다른 버전에서는 resolver 소스로 다시 확인한다.
coui 를 오래된 버전에서 최신까지 올린 실제 이관에서 반복해 겪은 것들이다. 증상이 "화면이 조금 달라졌다"로만 드러나는 것이 많아서, 탐지 방법을 같이 적는다. 버전 번호가 붙은 항목은 그 버전에서 확인한 것이다 — 다른 버전에서는 resolver 소스로 다시 확인한다.
A. 핀·릴리스#
| ID | 함정 | 증상 | 탐지 | 해결 |
|---|---|---|---|---|
| A1 | 같은 번호의 형제 태그가 다른 커밋 | pub get 은 되는데 컴파일에서 "missing implementations"(core 의 새 계약 멤버를 flutter·web 이 미구현) |
coui_pin.py check |
주 패키지 태그 커밋 SHA 하나로 형제 전부 고정 |
| A2 | 릴리스 커밋에 형제 태그가 없음 | 형제 태그를 찾다가 옛 태그를 고르거나 flutter 만 올림 | coui_pin.py plan 의 "태그가 없는 형제" 경고 |
SHA 사용 |
| A3 | /releases 첫 항목을 최신으로 봄 |
최신 릴리스를 놓치고 한 단계 전 세트로 올림 | 태그 semver 와 대조 |
주 패키지 태그(
coui_flutter-v*
·
coui_web-v*
) semver 최대 —
coui_pin.py plan --target latest
|
| A4 | CHANGELOG 하나만 읽음 | core 만 건드린 변경의 BREAKING(계약·토큰 변경)을 놓침 — 그런 항목은 core CHANGELOG 에만 실린다 | coui_pin.py breaking 으로 형제 합산 |
형제 CHANGELOG 모두 |
| A5 | 핀 주석·문서가 옛 핀을 "지금 핀"으로 적음 | 다음 작업자가 옛 값을 사실로 믿음 | 옛 SHA·태그 grep | 주석엔 ref 옆 한 줄만, 이력은 PR 로 |
| A6 | 태그 없는 임시 핀(스크래치 커밋)이 기본 브랜치로 들어감 | 재현 불가, 업스트림 기본 브랜치에 없는 커밋 | 목표 커밋에 git tag --points-at 결과가 없음 |
릴리스 태그가 생긴 뒤 교체. 임시 핀은 기능 브랜치에서만 |
B. 배율·크기 (coui_flutter — 웹은 web-apps.md §3)#
자세한 표는 scaling-and-defaults.md.
| ID | 함정 | 증상 | 탐지 | 해결 |
|---|---|---|---|---|
| B1 | 호출부가 준 padding·radius·height 에도 배율이 곱해짐 | 버튼·카드가 배율만큼 커짐(1.5 배율이면 1.5배) | 컴포넌트 골든, 크기 assert | 옛 값이 배율 없이 그려졌다면 px/배율 에 가까운 토큰 |
| B2 | Button.height·Tabs.itemHeight 는 최소 높이 |
24px 아이콘 버튼이 60px 높이, 탭 바가 커짐 | 크기 assert | 내용 크기로 정해지던 곳은 0 으로 해제 |
| B3 | 배율이 곱해지는 값과 안 곱해지는 값이 섞임 | 같은 화면에서 일부만 커짐(width·명시 fontSize 는 그대로) | resolver 소스 확인 | 값마다 곱해지는지 확인 후 보정 |
| B4 | coui Icon(Iconify) 크기에도 배율 |
24px 로 준 아이콘이 36px | 크기 assert | 원하는 px / 배율. 외부 아이콘 위젯은 안 곱해질 수 있어 따로 확인 |
| B5 | 토큰 이름은 비슷한데 값 체계가 다름 | 간격이 몇 배로 줄거나 늘어남(옛 단계 이름 s4=16px 대 새 space4=4px 류) |
L0 토큰 스냅샷 골든 | 이름 치환이 아니라 px 값표로 옮긴다 |
| B6 | 짝수 토큰 격자라 옛 값과 정확히 안 맞음 | 1~3px 차이 | 실측 | 가장 가까운 토큰 + 선택 근거를 주석에. 필요하면 사용자 결정 |
| B7 | 테스트 기본 환경으로 잼 | 형제 요소 높이를 잘못 알고 틀린 기준으로 맞춤 | 실기기 스크린샷과 대조 | 앱의 논리 폭·배율·실제 폰트를 로드한 조건으로 잰다 |
C. 기본값·스타일#
| ID | 함정 | 증상 | 탐지 | 해결 |
|---|---|---|---|---|
| C1 | 앱 소유 호환 래퍼를 걷으면 기본값이 바뀜 | 카드 테두리가 진한 색으로(outlined 기본 borderColor = outline), 구분선·배경 재등장 |
명시값 없는 호출부 grep + 골든 | 전역 컴포넌트 테마(variantStyles 등)로 옛 기본값 복원, 또는 수용을 승인 기록 |
| C2 | 폭 0 테두리도 헤어라인으로 그려짐 | borderWidth: 0 인데 1 물리픽셀 선이 보임 |
확대 캡처 | 선이 없어야 하면 색을 투명으로 하거나 테마 기본 색을 맞춤 |
| C3 | 카드 크기 기본값이 내용 크기 | 부모 폭을 무시하고 줄어듦 | 레이아웃 골든 | 전체 폭이어야 하는 곳은 sizing: .expand |
| C4 | 토큰에 알파를 먼저 적용 | 앱 팔레트가 아니라 coui 기본 팔레트 색으로 풀림 | 색 가드 테스트·골든 | 앱 색으로 푼 뒤(toValue()) 알파 적용 |
| C5 | CoreColor 와 Flutter Color 혼동 |
Object 로 추론돼 조용히 기본색, 또는 타입 에러 |
analyze + 골든 | 변수 타입을 명시하고 경계에서 toValue() |
| C6 | 스타일 노브가 선언만 되고 위젯이 소비하지 않음 | 값을 바꿔도 렌더 불변 | 값을 극단적으로 바꿔 렌더가 변하는지 동적 확인 | 앱 래퍼로 우회하고 업스트림에 이슈 |
| C7 | bool 플래그 하나가 스타일 외에 레이아웃 제약도 바꿈 | 텍스트는 맞는데 박스 크기가 달라짐 | 크기 assert | 플래그의 모든 효과를 소스로 확인 |
D. 텍스트 (coui_flutter — 웹은 web-apps.md §6)#
| ID | 함정 | 증상 | 탐지 | 해결 |
|---|---|---|---|---|
| D1 | 역할 없는 Text chain 이 주변 스타일을 상속(coui_flutter 0.150.0 BREAKING) | 역할 없이 굵기·색만 준 텍스트의 크기·색이 부모를 따라 바뀜 | 역할 없는 체인 grep(여러 줄 체인 포함) + 골든 | 예전처럼 보이려면 역할 명시(Text('제목').bodyMedium.semiBold) |
| D2 | 이미 스타일이 있는 child 에 체인 멤버를 붙이면 호출부 스타일을 덮음 | 컴포넌트 슬롯에 넘긴 텍스트 스타일이 무시됨 | 골든만 잡음 | 컴포넌트 안에서는 DefaultTextStyle.merge 로 감싼다 |
| D3 | 컴포넌트가 child 를 자기 본문 스타일로 다시 감쌈 | 조상에서 리셋해도 줄 높이·자간이 돌아옴 | 크기 assert | 컴포넌트의 본문 스타일 노브로 리셋하거나 자식에 명시 |
| D4 | 폰트 family 에 패키지 접두 누락 | 앱 폰트 대신 폴백 폰트로 오래 그려짐(골든도 폴백으로 찍혀 못 잡음) | 폰트 해석 테스트(실제 family 로 풀리는지 assert) | 폰트를 다른 패키지에 두면 packages/<pkg>/<Family> |
E. 아이콘·에셋#
| ID | 함정 | 증상 | 탐지 | 해결 |
|---|---|---|---|---|
| E1 | 새 coui Icon 은 첫 렌더에 네트워크(Iconify)에서 SVG 를 받음 |
첫 프레임 빈 박스, 오프라인·테스트에서 아이콘 없음. 웹은 디스크 캐시가 없어 매번 받음 | 오프라인 위젯 테스트 | 쓰는 아이콘을 번들해 시작 시 등록. 홉마다 재생성(coui 내부가 새 아이콘을 쓰기 시작할 수 있다) |
| E2 | 폰트 글리프 아이콘이 SVG 로 바뀌며 모양이 조금 달라짐 | 같은 이름 아이콘의 픽셀 차이 | 골든 | 번들로도 안 사라진다 — 승인 기록 |
| E3 | coui 가 새 이름을 export 해 앱 배럴과 충돌 | ambiguous_export, 엉뚱한 Icon 사용 |
analyze |
배럴
hide
/
show
조정. 웹도 같다 — coui_web 배럴은 jaspr 를 다시 내보내므로 앱이 jaspr 를 따로 import 하면 새 export 와 겹치는지 본다
|
F. 빌드·환경#
| ID | 함정 | 증상 | 탐지 | 해결 |
|---|---|---|---|---|
| F1 | coui 가 요구하는 Flutter 가 앱 고정값보다 높음 | pub get 실패 |
coui_pin.py plan 의 Flutter 요구 비교 |
SDK 를 먼저 따로 올린다 |
| F2 | 셸의 flutter 가 고정 버전이 아님 |
로컬에선 되는데 CI 에서 실패, 또는 반대 | flutter --version 과 고정 파일 비교 |
버전 관리자 래퍼(fvm flutter) 또는 고정 바이너리 경로 |
| F3 | coui 가 올린 네이티브 플러그인 제약이 연쇄 | Android/iOS/Windows 빌드 실패, 앱의 정확 버전 핀과 충돌 | 빌드 게이트 | 같은 PR 에서 연쇄 상향. 증분 빌드 캐시가 깨지면 캐시 정리 |
| F4 | 비공개 저장소 인증 | CI 에서만 pub get 실패 |
CI 로그 | 토큰 주입. 토큰 만료가 자동화 실패로만 드러날 수 있다 |
| F5 | analyze 가 못 잡는 컴파일 에러 | Flutter 는 flutter test 에서만, 웹은 배포 빌드(jaspr build)에서만 에러 |
test·배포 빌드를 게이트에 포함 | — |
G. 테스트·골든#
자세한 규칙은 golden-gate.md.
| ID | 함정 | 증상 | 탐지 | 해결 |
|---|---|---|---|---|
| G1 | 핀을 올린 뒤 일부 패키지 골든만 실행 | 기본 브랜치가 조용히 빨개짐(PR CI 가 없거나 느슨하면 특히) | 골든 보유 패키지 전수 실행 | test/golden 디렉토리 기준으로 전부 |
| G2 | 파일명으로 골든 테스트를 찾음 | 토큰 스냅샷·폰트 테스트처럼 이름에 golden 이 없는 파일을 놓침 | 디렉토리 기준 목록과 대조 | 디렉토리 기준 탐색 |
| G3 | 기본값을 검증하는 골든 셀이 그 기본값을 직접 명시 | 기본값 회귀가 골든을 통과 | 셀 리뷰 | 기본값 셀은 인자 없이 |
| G4 | flutter_test 는 그림자를 기본으로 끔 | 그림자 회귀가 골든에 안 보임 | 하네스 설정 확인 | 그림자를 볼 셀은 debugDisableShadows = false |
| G5 | 원래 깨져 있던 것과 이번 변경분을 섞음 | 원인 오판 | base 로 되돌려 같은 검사 | 전후를 나란히 비교 |
| G6 | 테스트가 구현 세부(전환 위젯 종류 등)를 찾음 | coui 가 내부 구현을 바꾸면 finder 실패 | test | 사용자 관점 finder 로 |
W. 웹(coui_web)#
Jaspr 웹 앱에만 해당한다. 절차와 배경은 web-apps.md.
| ID | 함정 | 증상 | 탐지 | 해결 |
|---|---|---|---|---|
| W1 | 앱의 jaspr override 가 coui_web 의 jaspr 제약보다 낮음 | pub get 은 통과하고 제약 위반이 숨겨짐 — 새 jaspr API 를 쓰는 coui 버전이면 컴파일·런타임 에러 |
목표 커밋 coui_web pubspec 의 jaspr 와 앱 선언·override·lock 대조 |
jaspr 계열을 함께 올린다(옛 핀 제약이 허용할 때만 먼저 따로). 정확 버전 핀·fork 연쇄와 추적 생성물도 함께 |
| W2 | 미리 컴파일한 CSS 를 재생성하지 않음 | 새 클래스·변수가 스타일 없이 나옴, 특정 렌더 분기만 깨짐 | 신선도 테스트 | 프로필의 재생성 명령 → 신선도 테스트 |
| W3 | coui Tailwind 설정을 다른 Tailwind 설정·테마 시트와 섞음 | 간격·색 스케일이 엉뚱함 | 페이지에 링크된 시트 확인 | coui 페이지는 coui 설정 하나. 리셋은 앱이 넣는다 |
| W4 | Flutter 의 itemHeight: 0 조언을 웹 Tabs 에 옮김 |
탭 높이 0 | SSR inline height |
웹 Tabs 높이는 고정값. 비우거나 원하는 값 |
| W5 | 아이콘이 뷰포트 진입 뒤 렌더됨 | 풀페이지 캡처 아래쪽 아이콘이 빈칸 | 캡처에서 <iconify-icon> 안 SVG 유무 |
아이콘 데이터 미리 싣기, 스크롤·대기 후 캡처 |
| W6 | SSR·하이드레이션 비교가 생성 id 로 흔들림 | 매 실행 스냅샷 diff, 하이드레이션 거짓 불일치 | 같은 코드로 두 번 렌더해 비교 | co-<컴포넌트>-<해시> id·<style> 셀렉터 정규화, 색 모드 명시 |
| W7 | 워크스페이스 밖 빌드(컨테이너 등)가 override 를 따로 만들며 형제를 빠뜨림 | 배포 빌드만 실패 | 배포 빌드 | coui_web·coui_core 를 같은 커밋으로, git 인증 주입 |
H. 코드모드·검색#
| ID | 함정 | 증상 | 탐지 | 해결 |
|---|---|---|---|---|
| H1 | 위젯 이름 grep 이 다른 위젯을 잡음 | Button\.x\( 가 IconButton.x( 까지 잡아 목록 오염 |
샘플 확인 | `([^A-Za-z.] |
| H2 | 코드모드가 주석·문자열까지 바꿈 | 주석·문구 오염 | diff 검토 | 파서 기반 변환 또는 주석·문자열 제외 |
| H3 | 짝을 이루는 API 중 하나만 옮김 | 열기는 되는데 닫기가 안 됨(한 원인이 수십 곳 실패) | 테스트 실패를 원인별로 묶기 | 짝 API 목록을 먼저 만든다 |
| H4 | 상태를 읽지 않는 스타일 헬퍼(예: 모든 상태 같은 값)를 살아 있는 분기로 오판 | 죽은 코드 보존 | 소스 확인 | — |
I. 운영(에이전트·사람 공통)#
| ID | 함정 | 해결 |
|---|---|---|
| I1 | 이슈가 열려 있다고 작업이 안 끝난 것은 아니다(반대도) | 코드 기준으로 상태 재확인 |
| I2 | 하위 에이전트의 "불가능"·"완료" 보고를 그대로 전달 | 소스·실행으로 확인 후 전달 |
| I3 | 코드 수정을 위임할 때 git 범위를 안 정함 | "커밋·푸시·PR·머지 금지"를 명시 |
| I4 | "값을 토큰으로 바꿔라"와 "그 값을 감싼 추상화를 없애라"를 혼동 | 전환 범위를 먼저 확인 |
| I5 | 정리(브랜치·워크트리 삭제) 패턴이 다른 작업까지 잡음 | 자기 실행 id 로 한정, 삭제 전 목록과 SHA 보존 |