From c89407a436060b82df468b08d184ab96b9324ba5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EA=B9=80=EC=88=98=ED=98=84?= Date: Wed, 21 Oct 2026 06:09:25 +0900 Subject: [PATCH] docs(sw): record the 2026-10-21 follow-up Unattended follow-up report: test alignment, Firefox fixes, shared helpers, source guard and public API checks, verification results and the commit split. --- docs/sw/sw-unattended-followup-2026-10-21.md | 210 +++++++++++++++++++ 1 file changed, 210 insertions(+) create mode 100644 docs/sw/sw-unattended-followup-2026-10-21.md diff --git a/docs/sw/sw-unattended-followup-2026-10-21.md b/docs/sw/sw-unattended-followup-2026-10-21.md new file mode 100644 index 0000000..73b170b --- /dev/null +++ b/docs/sw/sw-unattended-followup-2026-10-21.md @@ -0,0 +1,210 @@ +# SW 잔여 개선 통합 작업 (무인 작업, 2026-10-21) + +> 브랜치 `fix/test-home-menu-integration`, 시작 HEAD `96f7a84`(main `553e4df` + 테스트 정렬 3건 + 기준선 문서). §1–§10 작성 시점에는 Commit하지 않았고, 최종 마무리(§11)에서 기능별로 Commit했습니다. +> 실행 환경: Node.js 22.23.2 / npm 10.9.8, Chrome 155.0.8059.85(Playwright 1.62.1), Firefox 148.0.2(WebDriver BiDi). 결과 JSON·로그는 저장소 밖 임시 폴더에 두었고 기존 QA 폴더는 쓰지 않았습니다. +> 이전 단계: [회귀 기준선 문서](sw-test-regression-baseline-2026-10-20.md). `approvedMainCommit`은 `553e4df` 그대로입니다. + +## 1. 단계별 결과 + +| 단계 | 상태 | 요약 | +|---|---|---| +| 1. 테스트 정합성 | PASS | 기존 테스트 4종 미커밋 수정 유지·재검증, `menuOrder`·`hiddenControls` 공백을 테스트 전용 Fixture로 해소(Story 추가 없음) | +| 2. Firefox 안정화 | PASS | `Continuous` 잘림 원인 수정(CSS 1곳), Hover 흔들림 원인(부드러운 스크롤) 수정, 픽셀 비교 노이즈 원인 수정 | +| 3. 중복 개선 | PARTIAL | 키보드 Roving Focus 4곳·바깥 Pointer 닫기 4곳을 내부 Helper로 공통화. Slider·Escape·Viewer/Home Menu 닫기는 이유를 남기고 유지 | +| 4. SW Popup 하단 버튼 | 보류 | 일반 모드 공식 디자인 없음(문서 기록). Figma 작업 페이지는 MCP 전송 한도(4.5MB) 초과로 열람 불가 | +| 5. Storybook·AI 문서 | PASS | 공개 API·Story 무변경이라 AI 문서 수정 불필요(문서 설명과 실제 동작을 Fixture로 확인) | +| 6. 통합 검증 | PASS (설계상 예상 FAIL 5건 제외) | §7. 출처 검사 5건은 미승인 제품 변경을 정상 검출한 결과 | + +## 2. 테스트 정합성 (1단계) + +- 기존 4종(`dropdown-consolidation`, `storybook-cleanup-stage1`, `detail-buttons`, `property-panel-comparison`)과 `storybook-inventory.mjs` 수정은 그대로 두고 재검증했습니다(§7). +- **`header-composition.browser.mjs`(신규)** + `tests/fixtures/header-composition.tsx`: 공개 `@hm-design/sw` API만 쓰는 테스트 전용 화면을 esbuild로 묶어 검증합니다. 패키지 `files`(dist, ai)에 포함되지 않고 Storybook에도 등록되지 않습니다. + - 삭제된 Composition Story의 11개 프리셋 × 7폭(메뉴 순서·49px·넘침 없음·More 비어 있지 않음)을 같은 기대값으로 실행 + - 1920에서 숨긴 부품만 정확히 사라지는지(11개 부품), `home` 숨김 시 정적 로고 + - 잘못된 설정: `menuOrder` 중복·알 수 없는 ID·메뉴가 아닌 ID·빈 배열, `hiddenControls` 알 수 없는·중복 ID + - 실행 중 변경: 순서·숨김을 바꿔도 Callback 없음, 호스트 레이어 값 유지, 이동한 필드의 Dropdown 열기·Escape·Focus 복귀 + - 결과 161/161(3회 동일). 변조: 순서 무시 16 FAIL, 누락 메뉴를 앞에 배치 9 FAIL, `print` 숨김 무시 4 FAIL. 동작이 같은 변조 2건(중복 제거 삭제, 알 수 없는 ID 허용)은 결과가 바뀌지 않으므로 검출 대상이 아님을 확인했습니다. +- `detail-buttons` 상단 주석에 현재 대체 검증 위치를 추가했습니다. + +## 3. Firefox 안정화 (2단계) + +### 3.1 `Continuous` 말줄임 + +- 원인: 선스타일 값 행 `.hm-sw-header__line-values`(이름 + 가중치 38px + 스케일 38px)의 **flex 고유 폭(max-content)을 Firefox가 6.4px 작게 계산**(174.92 / Chromium 181.27). 글꼴(Noto 가변·Malgun), 굵기, 자간, text-shadow와 무관한 일정한 차이였고, 같은 마크업을 절대 위치로 복제하면 81.32px로 정상이었습니다. +- 수정(`packages/sw/src/theme/sw.css` 1곳): 같은 트랙의 grid로 변경 — `grid-template-columns: minmax(28px, auto) 38px 38px; gap: 12px`. 좁은 대표 필드에서 이름이 28px까지 줄며 말줄임하는 동작은 같습니다(Firefox 180px 강제 폭에서 flex와 grid 모두 87 / 28). +- Chromium 무변경 증명: 모든 SW Story × 5폭(41쌍)의 선스타일 필드 전체 요소 좌표 동일, PNG 동일(재캡처 2회 0건. 1회 차이는 상태 표본의 일시 차이로 재캡처에서 사라짐). +- Firefox: 58/59 → **59/59**(3회). +- Composed Controls `1.0000`의 38px 칸 결정은 바꾸지 않았습니다. + +### 3.2 Firefox Hover 흔들림 + +- 원인: Storybook 미리보기의 `html { scroll-behavior: smooth }`(`src/hm-theme.css`). `scrollIntoView` 직후 60ms 고정 대기로는 스크롤이 끝나지 않아 좌표를 잘못 읽고, 포인터가 다른 타일에 올라갔습니다(각 묶음의 첫 타일 「저장」, Footer 첫 칸 `LINE`). +- 수정(`hover-stability-firefox.browser.mjs`): `behavior: 'instant'` 스크롤, 고정 대기 대신 상태 대기(`:hover`·`:active` 상태와 전환 종료, 시간 초과는 FAIL로 기록). 놓은 뒤 Firefox가 `:active`를 다음 누름까지 유지하는 동작이 있어 마지막 대기만 `:hover`를 봅니다. 검사 항목은 줄이지 않았습니다. +- 결과 59/59(3회, 약 30초). 변조(Home Menu Hover 라벨 1px 이동): 42 FAIL. + +### 3.3 픽셀 비교 흔들림 + +- 같은 빌드끼리(HEAD 대 HEAD) 비교해 환경 노이즈를 먼저 분리했습니다. + - `integrated-visual`: Document Dropdown 3538px(Δ1), Responsive More 1–4px, Window Move Motion(재생 중 정지 시점 차이). Story 설명 문단의 글자 안티앨리어싱과 애니메이션 정지 시점이 원인이었습니다. + - `viewer-regression`: 36/39(Header 글자 노이즈, 속성창 4개 비교의 내부 스크롤 위치). +- 수정(허용 오차 0 유지): Chromium 실행 옵션(`--disable-gpu --font-render-hinting=none --disable-font-subpixel-positioning --disable-lcd-text --force-color-profile=srgb`), 재생 중이던 애니메이션은 시간 0으로 고정, 레이아웃 안정 대기, 내부 스크롤 요소는 즉시 스크롤 + 정수 위치. 페이지 높이만큼의 Viewport 방식은 측정형 Story를 오히려 흔들어 되돌렸습니다. +- 결과: `integrated-visual` 4회 캡처 0 DIFF(28장), `viewer-regression` HEAD 대 HEAD 39/39(3회). 변조(레이어 필드 padding 1px): 25/28 DIFF, 원복 후 0 DIFF. +- 통합 검증 중 같은 종류의 기존 결함 2건을 추가로 고쳤습니다: `theme-inheritance`(같은 실행 옵션, 22/23 → 23/23 3회), `text-weight`(Viewer More를 고정 200ms 뒤 확인 → 표시 대기, 실패 출력 중 예외 수정, 43/43 3회). 두 결함 모두 변경 전 빌드에서도 재현했습니다. + +## 4. 중복 개선 (3단계) + +| 대상 | 판단 | 내용 | +|---|---|---| +| Roving Focus(SWToolGroup, SWFooter, SWViewerHeader 탭, SWHomeMenu) | 공통화 | 같은 알고리즘 4벌 → 내부 `components/shared/roving-focus.ts`(`moveRovingFocus`). 패키지 진입점에서 export하지 않음. 키 집합·감싸기·선택(click) 동작을 호출부별로 그대로 전달 | +| 바깥 pointerdown 닫기(SWHeaderDropdown, LayerMenu, DocumentTitle, More) | 공통화 | 같은 effect 4벌 → 내부 `components/shared/use-outside-pointer.ts`(`useOutsidePointerDown`) | +| `SWViewerSlider` | 이미 공통 | `SWSlider appearance="panel"`을 감싸는 형태(기존) | +| 속성창 내부 Range | 유지 | 외형은 이미 `SWSlider appearance="property"`와 같은 CSS 규칙을 공유. 문자열 임시값 + Blur/Enter 확정 + Escape 취소 + 혼합값 처리와 86px 고정 폭이 `SWSlider`(change 시 확정, `flex 1 1 auto`, Disabled 투명도)와 달라 교체하면 동작·외형이 바뀜 | +| Escape·Focus 복귀 | 유지 | 컴포넌트마다 닫는 범위·Focus 복귀 대상·Popup 스택 처리가 다름 | +| SWViewerMenu·SWHomeMenu·SWFooter 바깥 클릭 | 유지 | Capture 단계, 하위 패널·More 상태 등 고유 조건이 있어 같은 Hook으로 바꾸면 동작이 바뀔 위험 | + +검증: 공개 타입 선언 비교(기존 `.d.ts` 전부 byte 동일, `components/shared` 추가뿐이며 진입점 미노출), 변경 전 빌드 대 변경 후 빌드 `viewer-regression` 39/39 픽셀 동일, 관련 Suite 전부 PASS. 기존 Suite가 끝 지점 감싸기와 "Focus를 옮기지 않는 바깥 pointerdown"을 확인하지 않는 것을 변조로 발견해 **`keyboard-dismiss.browser.mjs`(신규)** + `tests/fixtures/tool-group.tsx`를 추가했습니다: 변경 전 빌드 22/22, 변경 후 22/22(2회), Helper 변조 16 FAIL. + +## 5. SW Popup 하단 버튼 (4단계, 보류) + +- 근거: `packages/sw/ai/COMPONENT_CATALOG.md`·`DESIGN_RULES.md`에 "일반 모드 SW Popup 하단 버튼(적용·취소) 전용 디자인은 아직 없음", `sw-integrated-improvement-2026-10-19.md`에 "디자인 미확정 보류". Viewer 모드는 `SWViewerButton`(Viewer Figma 기준)이 있습니다. +- Figma `rj6wpsfk24lOWvWiVRAANP`의 페이지 목록(cover / 작업 / back / -----)은 확인했지만 작업 페이지 구조(약 4.5MB)는 MCP 응답 한도를 넘어 열람하지 못했습니다. +- 임의 디자인을 만들지 않았고 Popup 디자인은 그대로입니다. 권장: 일반 모드 Popup 하단 버튼 Figma Node를 지정받은 뒤 적용하거나, `SWViewerButton`을 일반 모드에도 쓸지 디자인 결정을 받습니다. + +## 6. 변경 파일 (미커밋) + +| 묶음 | 파일 | +|---|---| +| ① 테스트 정합성·Fixture | `packages/sw/tests/{dropdown-consolidation,storybook-cleanup-stage1,detail-buttons,property-panel-comparison}.browser.mjs`, `storybook-inventory.mjs`, `storybook-unused-removal.browser.mjs`(주석) — 이전 단계 / `header-composition.browser.mjs`, `fixtures/header-composition.tsx`(신규) | +| ② Firefox·픽셀·타이밍 안정화 | `packages/sw/src/theme/sw.css`(선스타일 값 행 grid), `packages/sw/tests/hover-stability-firefox.browser.mjs`, `integrated-visual.browser.mjs`, `viewer-regression.browser.mjs`, `theme-inheritance.browser.mjs`, `text-weight.browser.mjs` | +| ③ 중복 개선 | `packages/sw/src/components/shared/{roving-focus,use-outside-pointer}.ts`(신규), `sw-footer/SWFooterParts.tsx`, `sw-tool/SWTool.tsx`, `sw-viewer/SWViewerHeader.tsx`, `sw-home-menu/SWHomeMenu.tsx`, `sw-header/components/{SWHeaderDropdown,SWHeaderLayerMenu,SWHeaderDocumentTitle,SWHeaderMore}.tsx` / `packages/sw/tests/keyboard-dismiss.browser.mjs`, `fixtures/tool-group.tsx`(신규) | +| ⑤ 문서 | 이 보고서 | + +## 7. 통합 검증 (Node 22.23.2) + +최종 빌드(이 작업의 모든 변경 포함)에서 실행했습니다. 기존 실패와 신규 실패를 구분합니다. + +| 검사 | 결과 | 비고 | +|---|---|---| +| `npm run typecheck`(루트 `tsc -b` + 5개 Workspace) | PASS | | +| `npm run build-storybook`(theme 기본값 확인 + 5개 패키지 build) | PASS | OrgChart·OrgChart React·UI·Center Graphic·SW build 포함 | +| `npm run test:org-chart` / `test:components` / `test:guard` | 88/88 · 9/9 · 94/94 | | +| `npm run check:ai-docs` / `ai-docs.check.mjs` | PASS / 46/46 | | +| 공개 API(`packages/sw/dist/**/*.d.ts`) | 기존 46개 파일 byte 동일 | `components/shared`만 추가, 진입점 미노출 | +| Storybook index | 359개, ID·순서·내용 동일 | Story 추가·삭제 없음 | +| home-menu-integration | 23/23 | | +| overview / overlays | 53/54 / 19/20 | **예상 FAIL 1**: 미승인 제품 파일 3개 검출 | +| storybook-unused-removal | 92/93 | **예상 FAIL 1**: 같은 3개 | +| dropdown-consolidation / storybook-cleanup-stage1 | 227/228 / 40/41 | **예상 FAIL 1**: 같은 3개 | +| property-panel-comparison verify | 71/71 | | +| detail-buttons baseline · verify · preservation · capture | 1 · 123/123 · 15/15 · 1 | | +| header-composition(신규) / keyboard-dismiss(신규) | 161/161 / 22/22 | | +| viewer / footer-popup-viewer-refinement / footer-design-review | 70/70 / 47/47 / 21/21 | | +| integrated-improvement / range-cross-browser | 90/90 / 31/31 | | +| hover-stability Chromium / Firefox | 201/201 / 59/59 | Firefox 수정 전 58/59 | +| viewer-regression(변경 전 빌드 대 최종 빌드) | 39/39 | 픽셀 동일 | +| integrated-visual(그리드 수정 후·리팩터링 전 대 최종) | 28/28 동일 | | +| text-weight | 43/43(3회) | 첫 실행 중단 → **기존 타이밍 결함**(변경 전 빌드도 같은 위치 2/2 실패). 고정 200ms 대신 More 표시 대기로 수정, 실패 출력 중 예외도 수정 | +| theme-inheritance | 23/23(3회) | 첫 실행 22/23 → **기존 래스터 노이즈**(변경 전 빌드도 실패, 실행마다 다른 Story). 결정적 실행 옵션 추가 | + +- 신규 회귀: 없음. 예상 FAIL 5건은 모두 `SWFooterParts.tsx`·`SWHomeMenu.tsx`·`SWViewerHeader.tsx`(이번 미승인 리팩터링)만 가리키며 다른 검사는 PASS입니다. +- 미실행: `integrated-consumer`·`ai-template-consumer`(소비 프로젝트 빌드 필요), `overview-firefox`, `detail-buttons composition-history`, 외부 Viewer 저장소. + +## 8. 남은 문제와 확인 사항 + +- **출처 검사 FAIL(설계상 예상):** Overview·Overlays·미사용 검사·dropdown·stage1은 승인된 main 이후 `packages/sw/src` 변경을 FAIL로 표시합니다. 이번 미승인 제품 변경 중 `SWFooterParts.tsx`·`SWHomeMenu.tsx`·`SWViewerHeader.tsx`가 걸립니다. 나머지 변경 파일은 기존 `integratedChangedFiles` 예외에 포함되어 표시되지 않습니다. 승인 후 main 병합 → `approvedMainCommit` 갱신 절차([기준선 문서 §9](sw-test-regression-baseline-2026-10-20.md))로 해소합니다. 승인 목록에 임의로 넣지 않았습니다. +- ~~**출처 검사의 사각:** 위 검사는 `git diff `만 보므로 새로 추가한 미추적 파일(예: `components/shared/*.ts`)은 감지하지 못합니다.~~ → §10에서 해결. +- `integratedChangedFiles` 예외 범위가 넓어 SWHeader 하위 파일 변경이 출처 검사에 드러나지 않습니다(기준선 문서 §10과 같은 사항). +- `detail-buttons composition-history` 모드는 c8e5fbe 이전 빌드가 필요해 실행하지 않았습니다(현재 대체: `header-composition`). +- 외부 Viewer 소비 프로젝트(별도 저장소·Submodule)는 열지 않았습니다. 공개 API 무변경이라 영향은 없을 것으로 보지만 **미검증**입니다. `integrated-consumer`·`ai-template-consumer`(소비 프로젝트 빌드)도 이번에 실행하지 않았습니다. +- Docker·Gitea Workflow·다른 프로젝트는 건드리지 않았습니다. + +## 9. 권장 Commit 분리 + +1. `test(sw): align legacy storybook suites and add header composition fixture` — ① +2. `fix(sw): keep the line style name uncut in Firefox` — `sw.css` / `test(sw): stabilize Firefox hover, pixel and resize timing checks` — 테스트 5개 +3. `refactor(sw): share roving focus and outside pointer handling` — ③ 제품 파일 + `keyboard-dismiss` 테스트 +4. `test(sw): detect untracked and moved files in source guards` — `source-changes.mjs`(신규), `overview`·`storybook-unused-removal` 가드 부분(§10). `dropdown-consolidation`·`storybook-cleanup-stage1`은 ①의 수정과 같은 파일이라 ①에 함께 넣거나 `git add -p`로 가드 부분만 분리합니다. +5. `docs(sw): record the 2026-10-21 follow-up` — 이 보고서 + +## 10. 후속: 소스 가드 신규 파일 검출 보완 (2026-10-21) + +> 실행 환경: 이 PC에는 Node.js 22.23.2가 없어 시스템 Node.js **20.20.1**로 실행했습니다(시스템 Node 설정은 바꾸지 않음). 가드 5종은 Chromium만 쓰고 전역 `WebSocket`이 필요 없습니다. 제품 소스·Storybook 구성·공개 API는 바꾸지 않았으므로 §7의 빌드·브라우저 결과는 재실행하지 않고 그대로 유효합니다(제품 소스 최종 수정 01:50, `storybook-static` 01:50:50·`dist` 02:01 빌드 이후 변경 없음). + +### 10.1 변경 + +- **`packages/sw/tests/source-changes.mjs`(신규):** `sourceChanges(ref, paths)` = `git diff --name-status -M `(Staged·미Staged 수정, 추가, 삭제, 이름 변경의 양쪽 경로) + `git ls-files --others --exclude-standard`(미추적 파일, `.gitignore` 대상인 `dist`·`storybook-static`·`*.log` 제외). 항목마다 `status`(M/A/D/T/R/?)와 `staged`를 기록합니다. `unapprovedChanges(ref, paths, approved)`는 승인 목록(`integratedChangedFiles`, 정확한 경로)에 없는 항목만 돌려줍니다. +- `overview.browser.mjs`(두 모드), `storybook-unused-removal.browser.mjs`, `dropdown-consolidation.browser.mjs`, `storybook-cleanup-stage1.browser.mjs`: `approvedMainCommit` 대비 검사와 `sinceBaseline` 집계만 위 헬퍼로 교체했습니다. 검사 경로, 단언, 승인 목록, `approvedMainCommit`(`553e4df`), 두 Commit 사이 비교(stage1·consolidation)는 그대로입니다. + +### 10.2 결과 + +| 검사 | 결과 | 판정 | +|---|---|---| +| 원본 저장소 overview / overlays / unused / dropdown / stage1 | 53/54 · 19/20 · 92/93 · 227/228 · 40/41 | 수치는 §7과 같음. FAIL은 가드 1건씩이며 이제 미승인 파일 **5개**(수정 3 + 미추적 `components/shared/roving-focus.ts`·`use-outside-pointer.ts`)를 표시 → **승인 전 변경을 정상 검출한 예상 FAIL** | +| 임시 Worktree(HEAD, 제품 소스 = 승인 main) 5종 전체 | 54/54 · 20/20 · 93/93 · 228/228 · 41/41 | 정상 상태에서 불필요한 FAIL 없음 | +| 임시 Worktree 혼합 변조(승인 `sw.css` + 미승인 수정 + 미추적 신규) overview | 53/54 | 미승인 2개만 표시, 승인 파일 제외 | +| 헬퍼 변조 16개 시나리오 × 가드 경로 4종 | 64/64 기대값 일치 | 아래 | + +변조 시나리오(기존 방식 → 새 방식 검출 수): 미추적 신규 소스 0 → 1, 신규 모듈의 공개 export 0 → 1, 일반 이동 1 → 2(삭제 + 새 경로), `git mv` 1 → 2, 혼합 1 → 2, 다른 패키지(`packages/ui`) 수정 + 신규 1 → 2(unused 가드만 해당 경로 검사). 수정·Staged 수정·삭제·`git rm`·`git add` 신규는 기존과 같이 1. 승인 파일만 수정, 무시 대상(`dist`, `*.log`)만 생성, 승인 파일만 있는 상태는 0(오검출 없음). 승인 파일을 새 경로로 옮기면 새 경로를 미승인으로 표시합니다. + +### 10.3 남은 사항 + +- `packages/sw/src/index.ts`는 `integratedChangedFiles`에 있어 이 가드들은 그 파일의 export 추가를 검출하지 않습니다(변조 5b, 기존 정책 그대로). `ai-docs.check.mjs`는 AI 문서에 나온 이름이 실제 export인지만 확인하므로, 진입점 export 추가를 자동으로 막는 가드는 없습니다(§4의 `.d.ts` 비교는 수동 실행). 예외 범위 축소는 승인 목록 정책 변경이라 이번에 하지 않았습니다. +- Node.js 22.23.2 환경 재확인은 미검증입니다. + +## 11. 최종 마무리: 공개 API 검사·Commit (2026-10-21) + +> 실행 환경: Node.js **20.20.1**(Node 22.23.2 재검증은 사용자 결정으로 제외). 제품 소스·Storybook 구성은 §7 이후 바뀌지 않았고(`packages/sw/src`·`src/sw`·`.storybook`에 `dist`(02:01)·`storybook-static`(01:50:50)보다 새 파일 없음) §7의 제품 회귀 결과를 재사용했습니다. + +### 11.1 공개 API 검사 (`packages/sw/tests/public-api.check.mjs`, 신규) + +§10.3의 사각(`src/index.ts`가 `integratedChangedFiles`에 있어 진입점 export 변경을 출처 검사가 보지 않음, `.d.ts` 비교는 수동)을 보완합니다. 승인 목록 정책(`integratedChangedFiles`)은 바꾸지 않았습니다. + +- 기준: `public-api.approved.json`. 저장소 밖에서 `553e4df`(`approvedMainCommit`)의 `packages/sw`를 `git archive`로 꺼내 build(`SW_BUILD_COMMIT=553e4df…`)하고 `--snapshot`으로 만들었습니다. 현재 HEAD에서 만든 값이 아닙니다. 170개 export(실행 값 64, 타입 106), 진입점에서 닿는 `.d.ts` 42개. +- 검사(빌드된 `dist`와 `package.json` 기준, 7항목): + 1. 기준 Commit이 `approvedMainCommit`과 HEAD의 조상 + 2. `package.json`의 `name`·`exports`·`main`·`types`·`module`·`files` 동일 + 3. `exports` 대상 파일이 `dist`에 존재 + 4. 진입점 export 이름 집합 동일(추가·삭제, 이름 변경은 삭제 + 추가로 표시) + 5. export 종류(실행 값 / 타입 전용) 동일, `.d.ts`에 선언된 값이 `dist/index.js`에 실제로 있음(`import()`로 확인) + 6. `dist/index.d.ts`에서 상대 import로 닿는 `.d.ts` 파일 목록 동일(내부 모듈의 새 노출 검출) + 7. 그 파일들이 byte 단위로 동일(공개 타입과 공개 타입이 참조하는 내부 타입의 변경 검출) +- 진입점에서 닿지 않는 내부 모듈(`components/shared/*`)의 추가, 구현만 바꾼 리팩터링은 통과합니다. TypeScript 7.0.2에는 JS Compiler API가 없어 선언 파일을 정적으로 따라가고 실행 export는 실제 `dist/index.js`로 확인합니다. +- 검사는 기준 파일을 고치지 않습니다. 공개 API를 바꾸는 승인된 변경에서만, 그 Commit의 build로 `--snapshot`을 다시 실행해 같은 Commit에 넣습니다. `approvedMainCommit`만 옮길 때는 기준 파일을 바꾸지 않습니다(1번 항목이 조상 관계로 연결). +- 다른 패키지: 이번 범위에서 `packages/ui`·`org-chart`·`org-chart-react`·`center-graphic`은 바뀌지 않았고(`storybook-unused-removal` 경로 검사), 새 검사를 만들지 않았습니다. + +| 실행 | 결과 | +|---|---| +| 현재 공유 `dist`(최종 제품 소스 build) | 7/7 PASS | +| 변조 0: 현재 소스를 저장소 밖에서 다시 build | 7/7 PASS | +| 변조 1: 쓰이지 않는 내부 모듈 `components/shared/extra-internal.ts` 추가 | 7/7 PASS(정상 유지) | +| 변조 2: `index.ts`에서 `moveRovingFocus` export | 4/7 — `added: moveRovingFocus`, `newlyReachable: components/shared/roving-focus.d.ts`, `index.d.ts` 변경 | +| 변조 3: `SWSelect` 값 export 삭제 | 5/7 — `removed: SWSelect` | +| 변조 4: `SWSlider as SWRangeSlider` | 5/7 — `added: SWRangeSlider`, `removed: SWSlider` | +| 변조 5: `SWSliderProps`에 선택 prop 추가 | 6/7 — `components/sw-slider/SWSlider.d.ts` 변경 | +| 변조 6: `package.json`에 `./shared` subpath 추가 | 5/7 — `exports` 변경, 대상 파일 없음 | + +변조는 모두 저장소 밖 복사본에서 build해 실행했고 공유 `dist`·`storybook-static`은 건드리지 않았습니다. + +### 11.2 소스 가드 5종 (Commit 전, Node 20.20.1) + +| 검사 | 결과 | 검출 파일 | +|---|---|---| +| overview / overlays | 53/54 / 19/20 | 5개 동일 | +| storybook-unused-removal | 92/93 | 5개 동일 | +| dropdown-consolidation | 227/228 | 5개 동일 | +| storybook-cleanup-stage1 | 40/41 | 5개 동일 | + +검출 파일은 `SWFooterParts.tsx`·`SWHomeMenu.tsx`·`SWViewerHeader.tsx`(수정)와 `components/shared/roving-focus.ts`·`use-outside-pointer.ts`(미추적 신규)뿐이며, 모두 §4의 승인된 중복 개선 범위입니다(diff 확인: Roving Focus 4곳·바깥 pointerdown 4곳의 같은 알고리즘을 내부 Helper 호출로 교체). 다른 FAIL은 없습니다. 승인 목록에 넣지 않고, main 병합 후 기준선 문서 §9 절차로 `approvedMainCommit`을 옮겨 해소합니다(§12). + +### 11.3 Commit (작성자·Committer 모두 `김수현 `, `.git/config` 기준) + +| Commit | 내용 | 파일 | +|---|---|---| +| `1e05eaf` | test(sw): detect untracked source files and public API changes | `source-changes.mjs`, `public-api.check.mjs`, `public-api.approved.json`, `overview.browser.mjs`, `storybook-unused-removal.browser.mjs`(가드 부분) | +| `b50e95c` | test(sw): align legacy storybook suites and add header composition fixture | `dropdown-consolidation`, `storybook-cleanup-stage1`, `detail-buttons`, `property-panel-comparison`, `storybook-inventory.mjs`, `storybook-unused-removal`(주석), `header-composition.browser.mjs`, `fixtures/header-composition.tsx` | +| `1ffff67` | fix(sw): keep the line style name uncut in Firefox | `packages/sw/src/theme/sw.css` | +| `af7f98d` | test(sw): stabilize Firefox hover, pixel and timing checks | `hover-stability-firefox`, `integrated-visual`, `viewer-regression`, `theme-inheritance`, `text-weight` | +| `7cc3aa1` | refactor(sw): share roving focus and outside pointer handling | `components/shared/*.ts`(신규 2), 제품 파일 8 | +| `0f9e9b0` | test(sw): cover roving focus wrap and outside pointer dismissal | `keyboard-dismiss.browser.mjs`, `fixtures/tool-group.tsx` | + +`dropdown-consolidation`·`storybook-cleanup-stage1`의 정렬 수정은 `source-changes.mjs`를 쓰므로 가드 Commit을 먼저 두었습니다. `storybook-unused-removal`은 가드 부분과 주석을 나눠 Stage했습니다. 기존 미추적 16개(`.claude/launch.json`, `docs/sw/home-menu-*-qa-2026-10-19/*.png` 15개)는 Commit하지 않았습니다.