fix(sw): stabilize theme regression test and update integration docs

This commit is contained in:
shkim4 committed 2026-10-20 12:47:48 +09:00
1 parent 73e30242f2
commit 47972c55ff
11 files changed
+186 -23

No files matched your search

+6 -5
View File
@@ -38,10 +38,11 @@
필요할 때만 [보존된 상세 지침](docs/guidelines/ai-implementation-details.md)을 읽는다. 신규 화면에는 위 필수 규칙이 우선한다.
## 공식 작업 경로와 Git 정책 — 2026-10-11 사용자 확정
## 작업 위치와 Git 정책 — 2026-10-20 개정 (2026-10-11 정책 대체)
- HMDesign 실제 수정은 `D:\디자인시스템\HMDesign-Gitea-Transfer`에서 진행한다. 시작 전 절대경로·`main` 브랜치·공식 origin `https://gitea.hmac.kr/b25027/HMDesign.git`·최신 원격 HEAD·Working Tree·기존 미커밋 변경을 확인한다.
- 퍼블리셔 요청에 필요한 부분만 수정하고 기존 Component·Pattern·Theme·Storybook을 보존한다. 변경 영향과 관련 MD/Storybook 정합성을 검증하고 유효한 기존 정상 검증은 불필요하게 반복하지 않는다.
- 필수 검증을 통과하면 관련 파일만 Stage하고 Staged Diff를 검토한 뒤 Commit·공식 Gitea `main` 일반 Push를 수행한다. 작업별 사용자의 명시적 Commit/Push 보류 지시가 있으면 그 지시를 우선한다.
- Commit/Push 직전에 원격 `main`을 다시 확인한다. 시작 기준 이후 원격이 변경됐거나 중요한 충돌이 있으면 자동으로 덮어쓰지 않고 중단한다. 완료 후 Local/Remote SHA와 Working Tree를 확인한다.
- 작업 위치는 고정 폴더가 아니라 **사용자가 지정한 브랜치의 작업 트리**다(별도 Clone·worktree 포함). 시작 전 절대경로·현재 브랜치·HEAD·공식 origin `https://gitea.hmac.kr/b25027/HMDesign.git`·최신 원격 HEAD·Working Tree·기존 미커밋 변경을 확인한다. 같은 폴더를 다른 작업자나 AI가 수정 중일 수 있으므로 수정 전 파일을 다시 읽는다.
- 기능 작업은 기능 브랜치(예: `feature/sw-package`, `feature/orgchart-package`)에서 진행하고 `main`에는 사용자가 지시한 병합(PR 등)으로만 반영한다. `main` 직접 Commit/Push와 병합은 사용자의 명시적 지시가 있을 때만 수행한다.
- 요청에 필요한 부분만 수정하고 기존 Component·Pattern·Theme·Storybook을 보존한다. 변경 영향과 관련 MD/Storybook 정합성을 검증하고 유효한 기존 정상 검증은 불필요하게 반복하지 않는다.
- Commit·Push는 사용자 승인 후 수행한다. 관련 파일만 Stage하고 Staged Diff를 검토한다. 직전에 대상 원격 브랜치를 다시 확인하고, 시작 기준 이후 원격이 변경됐거나 중요한 충돌이 있으면 자동으로 덮어쓰지 않고 중단한다. 완료 후 Local/Remote SHA와 Working Tree를 확인한다.
- `main` push는 웹 Storybook(`https://design.hmac.kr/`) 배포 Workflow를 실행한다(`.gitea/workflows/deploy-storybook.yml`). 병합 전에 운영 웹을 변경하지 않는다.
- GitHub 이전 원본 폴더·remote에는 수정/Push하지 않는다. Force Push·Reset·Clean·기존 이력 삭제·백업 브랜치 변경·무관한 파일 Stage를 금지한다.
+1 -1
View File
@@ -63,7 +63,7 @@ shadcn/ui(Radix, Tailwind v4, CVA) 기반 HM 디자인 시스템 시제품. Reac
- Button(`variant`=역할, `appearance`=treatment, `size`), Input(`size`, native 상태 selector), Select(Trigger `size`, Radix 상태 속성)는 ui_viz Confirmed 규격으로 구현했다. Card도 `<Card />`만으로 HM 스타일이 적용된다(ui_viz Content Card, `packages/ui/src/theme/card.css`). Dialog도 `<DialogContent />`만으로 HM 스타일이 적용된다(`packages/ui/src/theme/dialog.css`).
- 구조(2026-10-02 패키지 분리, npm workspaces): UI 컴포넌트·Theme 구현은 `packages/ui`(`@hm-design/ui`, 컴포넌트 `src/components/ui/`, Theme `src/theme/*.css`, 진입점 `src/theme.css`·`src/tailwind.css`), 컴포넌트 문서는 `docs/components/`, Storybook·Demo는 루트(`.storybook/`, `src/`; Demo 스타일은 `src/hm-theme.css`), `packages/center-graphic`(`@hm-design/center-graphic`)은 CENTER 구현(제목·정보·노선/공간·시설물), 기준 문서는 `docs/center-graphic/`다.
- 작업 순서: 컴포넌트 문서 정리, Card, Dialog, 패키지 분리, CENTER 구현, 1단계 UI 컴포넌트(Textarea, Tabs, Label, Checkbox, Radio Group, Switch, Badge, Alert, Separator)와 2단계 A묶음 Floating(Dropdown Menu, Popover, Tooltip, Hover Card)과 B묶음 Modal(Sheet, Drawer(vaul), Alert Dialog)과 C묶음 Expand(Accordion, Collapsible)과 D묶음 Navigation(Breadcrumb, Pagination, Navigation Menu, Menubar)까지 완료로 2단계 UI가 끝났다. 3단계 A묶음 Data/Feedback(Table, Progress, Skeleton, Avatar, Scroll Area)과 B묶음 Selection Controls(Slider, Toggle, Toggle Group)과 C묶음 Search/Date(Calendar, Command, Date Picker, Combobox)까지 완료(새 dependency: `react-day-picker`, `cmdk`)과 D묶음 Layout/Media(Carousel, Aspect Ratio, Resizable)까지 완료로 3단계 UI가 끝났다(새 dependency: `embla-carousel-react`, `react-resizable-panels`). floating surface Semantic 정합성 작업도 완료했다. 4단계 A묶음 독립 UI(Context Menu, Toast/Sonner)까지 완료(새 dependency: `sonner`). 4단계 B묶음 업무 Pattern(Data Table, Filter Bar)까지 완료(새 dependency: `@tanstack/react-table` v9)로 4단계 핵심이 끝났다. Layout Pattern은 GNB·LNB·Page Header·Dashboard Layout까지 완료(`docs/patterns/gnb.md`, `lnb.md`, `page-header.md`, `dashboard-layout.md`, ui-viz 시각 기준)했다. 각 Navigation Mode는 독립 레이아웃을 소유한다. GNB ONLY는 기존 LNB 메뉴 부품을 재사용하는 내부 Sidebar를 허용하며 외곽 배경만 Green Light 2로 적용한다. DASHBOARD ONLY 운영 화면은 전용 Header(48px)·업무 상태 필터 + Content로 구성하고 Sidebar를 금지한다. 운영 최소 폭 960px·내부 가로 스크롤을 유지한다. 기존 DashboardSidebar Export·sidebar slot·1232px 구성은 호환성·검증용이며 신규 운영 Reference가 아니다. LNB 3뎁스(`LNBGroup` 중첩, 2026-10-06)와 모바일 Sheet 중첩 그룹까지 완료(`docs/patterns/lnb.md` §3a). 현재 LNB 모바일은 독립 LNB Mobile Header와 Sheet를 사용한다(`lnb.md` §3b). 과거 GNB+LNB 조합·Brand ownership·gradient 접점 기준은 Navigation Single Mode Policy로 대체되었다. 한 페이지에는 GNB / LNB / DASHBOARD 중 하나만 사용하며 기존 혼합 Story는 verification / VERIFICATION ONLY 조건의 기술 검증에만 한정한다. 경로: UI primitive `packages/ui/src/components/ui`, Pattern `packages/ui/src/patterns`, Pattern 문서 `docs/patterns`. Field는 코드 없이 `docs/components/field.md` 소비 가이드만 둔다.
- 두 패키지는 서로 의존하지 않으며 `private: false`, compiled dist export(JS·타입·CSS)와 library build가 구현되어 있다. Registry publish 이력은 ZIP 검증 범위 밖이다. Runtime Theme URL Loader와 Theme JSON 독립 산출은 구현되었고 실제 CDN infrastructure/publish pipeline은 미구현이다. CENTER 기준은 `docs/center-graphic/README.md`, `packages/center-graphic/src/`, `src/center/`의 현재 HM 구현과 대표 Story다. 외부 ui_viz owner 자료는 제공된 경우에만 참고하며 필수 실행 경로가 아니다. UI와 CENTER 규격을 섞지 않는다.
- 패키지는 5개다: `@hm-design/ui`(Web), `@hm-design/sw`(SW 일반·뷰어 모드, `packages/sw`, Story `src/sw/`, 문서 `docs/sw/`), `@hm-design/org-chart`·`@hm-design/org-chart-react`(OrgChart, Story `src/org-chart/`, 문서 `docs/org-chart/`), `@hm-design/center-graphic`(CENTER). 역할·진입점 표는 `README.md`의 패키지 구성, 외부 설치는 `docs/guidelines/external-install-submodule.md`를 따른다. `org-chart-react`가 Core에 의존하고 `/hm-dialog`만 `@hm-design/ui`를 optional peer로 쓰는 것 외에는 서로 의존하지 않는다. 모두 private이 아니며 compiled dist export(JS·타입·CSS)와 library build가 구현되어 있다. Registry publish 이력은 ZIP 검증 범위 밖이다. Runtime Theme URL Loader와 Theme JSON 독립 산출은 구현되었고 실제 CDN infrastructure/publish pipeline은 미구현이다. CENTER 기준은 `docs/center-graphic/README.md`, `packages/center-graphic/src/`, `src/center/`의 현재 HM 구현과 대표 Story다. 외부 ui_viz owner 자료는 제공된 경우에만 참고하며 필수 실행 경로가 아니다. UI와 CENTER 규격을 섞지 않는다.
> 위 작업 순서는 구현 이력이다. 현재 API·제약은 코드와 Component/Pattern 문서를 대조한다.
+33 -5
View File
@@ -1,6 +1,20 @@
# HM Design System
React + TypeScript + Vite와 shadcn/Radix 기반 HM 디자인 시스템입니다. UI Component·업무/Layout Pattern·CENTER Graphic을 독립 workspace 패키지와 통합 Storybook으로 확인합니다.
React + TypeScript + Vite와 shadcn/Radix 기반 HM 디자인 시스템입니다. Web UI·SW UI·조직도·CENTER Graphic을 독립 workspace 패키지와 통합 Storybook으로 확인합니다.
## 패키지 구성
npm workspace(`packages/*`)의 패키지 5개입니다. 서로 의존하지 않으며(아래 표의 예외 제외) 각자 `dist/`의 ESM JS·타입·CSS를 `exports`로 공개합니다.
| 패키지 | 역할 | 공개 진입점 | Storybook | AI 지침 |
|---|---|---|---|---|
| `@hm-design/ui` | **Web** 일반 웹 UI Component(shadcn/Radix)·업무/Layout Pattern·HM Theme Runtime | `.`, `/theme`, `/theme.css`, `/tailwind.css`, `/dashboard-status.css` | Foundation · Components · Patterns · Theme | `packages/ui/ai/` |
| `@hm-design/sw` | **SW** 설치형·도면 SW UI. SW 일반 모드(`SWHeader`·`SWPopup`·`SWFooter`)와 SW 뷰어 모드(`SWViewer*`), 공통 범위 `SWTheme`. `@hm-design/ui`와 독립 | `.`, `/styles.css` | SW | `packages/sw/ai/` |
| `@hm-design/org-chart` | **OrgChart** Core(Vanilla, Shadow DOM, 데이터·배치·상호작용) | `.`, `/style.css`, `/engine.css`, `/browser` | OrgChart | `packages/org-chart/ai/` |
| `@hm-design/org-chart-react` | OrgChart React Wrapper. Core `@hm-design/org-chart`에 의존하고, 선택 Entry `/hm-dialog`만 `@hm-design/ui`(optional peer)를 사용 | `.`, `/hm-dialog` | OrgChart | `packages/org-chart-react/ai/` |
| `@hm-design/center-graphic` | CENTER 그래픽(제목·정보·노선/공간·시설물). 내부 검토 범위, License 미확정 | `.`, `/reference`, `/style.css` | Center Graphic | 없음(README) |
다른 프로젝트에서의 설치는 [외부 설치 가이드(Git Submodule + npm Workspace)](docs/guidelines/external-install-submodule.md)를 따릅니다.
## Navigation 정책
@@ -28,7 +42,7 @@ Storybook은 기존 HM Component·Pattern·Theme·Variant·State·Interaction을
최신 [Generic Status Error](docs/reports/generic-status-error-token-2026-10-11.md)는 `#B3261E` / Provisional이며 기존 Destructive `#D32F2F`를 유지합니다. [Disabled Input Label](docs/reports/disabled-input-label-2026-10-11.md)은 승인된 Input Story 소비 범위 60%, 공통 Label은 50%입니다. [Background Gradient 기록](docs/reports/brown-orange-background-gradients-2026-10-11.md)과 [현재 계약](docs/guidelines/runtime-theme-contract.md)은 Green·Brown·Orange 공통 배경을 구분하며, Foundation Color의 Button 전용 미리보기는 제거하고 실제 Button·Dashboard Gradient는 유지합니다. Focus `#266651`은 시험 적용 상태입니다.
Node.js는 lockfile의 `^20.19.0 || >=22.12.0` 조건을 충족해야 합니다(Node 21 제외). 이번 재현 환경은 **Node 20.20.1 / npm 10.8.2**입니다. Node 설치 시 제공되는 npm과 PATH를 확인하고, 재현 시에는 검증한 버전을 사용합니다. 최초 설치에는 공식 Gitea와 npm 레지스트리 접근이 필요합니다.
Node.js는 lockfile의 `^20.19.0 || >=22.12.0` 조건을 충족해야 합니다(Node 21 제외). 검증 기록은 **Node 20.20.1 / npm 10.8.2**(2026-10-11)와 **Node 22.23.2 / npm 10.9.8**(2026-10-20, SW 포함 전체 build·Storybook build)입니다. Docker 이미지는 `node:24-alpine`에서 빌드하며 Docker 경로는 아직 실제 빌드로 검증하지 않았습니다. Node 설치 시 제공되는 npm과 PATH를 확인하고, 재현 시에는 검증한 버전을 사용합니다. 최초 설치에는 공식 Gitea와 npm 레지스트리 접근이 필요합니다.
```powershell
git clone https://gitea.hmac.kr/b25027/HMDesign.git
@@ -71,7 +85,7 @@ docker compose up -d --build
### Gitea Actions 배포
`.gitea/workflows/deploy-storybook.yml`은 `main`에 push되거나 수동 실행될 때 해당 실행 커밋의 소스 archive를 SSH로 배포 서버에 보내고, 서버에서 Docker build와 Compose 갱신을 수행합니다. 기존 Git 작업 트리는 건드리지 않고 빌드는 서버에서 이뤄지며, 빌드 산출물은 Actions Runner에서 전송하지 않습니다. 실행 커밋별 소스는 `.storybook-deploy/releases/`에 보관하고 서비스는 배포 서버의 **6024 포트**를 사용합니다. 새 이미지 빌드가 끝난 뒤 Compose가 컨테이너를 교체하므로 빌드 중에는 현재 서비스를 유지합니다.
`.gitea/workflows/deploy-storybook.yml`은 `main`에 push되거나 수동 실행될 때 해당 실행 커밋의 소스 archive를 SSH로 배포 서버에 보내고, 서버에서 Docker build와 Compose 갱신을 수행합니다. 기존 Git 작업 트리는 건드리지 않고 빌드는 서버에서 이뤄지며, 빌드 산출물은 Actions Runner에서 전송하지 않습니다. 실행 커밋별 소스는 `.storybook-deploy/releases/`에 보관하고 서비스는 배포 서버의 **6024 포트**를 사용합니다. 새 이미지 빌드가 끝난 뒤 Compose가 컨테이너를 교체하므로 빌드 중에는 현재 서비스를 유지합니다. 따라서 웹 Storybook(`https://design.hmac.kr/`)은 `main` 배포본이며, `main`에 병합되지 않은 브랜치(예: SW 패키지 작업 브랜치)의 Story는 표시되지 않습니다.
저장소 Actions Variables에 `DEPLOY_HOST` (`10.13.10.4`), `DEPLOY_USER` (`user`), `DEPLOY_PATH` (`/home/user/services/HMDesign`), `DEPLOY_PORT` (`22`), `DEPLOY_KNOWN_HOSTS` (배포 서버의 검증된 SSH host key)를 설정하고, Actions Secret에 `DEPLOY_SSH_KEY`를 등록합니다. 배포 서버 계정은 지정한 경로에 `.storybook-deploy/releases/`를 만들고 Docker Engine/Compose plugin을 실행할 권한이 필요합니다. Actions Runner는 SSH와 SCP로 배포 서버에 접속할 수 있어야 합니다.
@@ -95,6 +109,9 @@ npm run dev:publisher-example
import { Button } from "@hm-design/ui"
import { CenterTitle, TunnelFacility } from "@hm-design/center-graphic"
import { DataTable, FilterBar, useDataTable } from "@hm-design/ui" // Patterns: docs/patterns/
import { SWTheme, SWHeader, SWFooter } from "@hm-design/sw" // SW 일반 모드
import { SWViewerHeader, SWViewerPanel } from "@hm-design/sw" // SW 뷰어 모드
import { OrgChart } from "@hm-design/org-chart-react" // Core: @hm-design/org-chart
```
```css
@@ -107,9 +124,11 @@ import { DataTable, FilterBar, useDataTable } from "@hm-design/ui" // Patterns:
별도 React 앱은 [Tailwind v4 검색 범위 가이드](docs/guidelines/runtime-portability.md#21-tailwind-v4-소비자-검색-범위)를 확인합니다. `source("./")`는 CSS 위치에 따라 범위가 달라지며 HM `tailwind.css`의 명시 source를 유지해야 합니다. 공개 package Theme는 동봉된 `dist/fonts/`를 상대 URL로 참조합니다. 개발 저장소·포터블 ZIP·React 소비자·단일 HTML의 차이는 [폰트·Asset 경로](docs/guidelines/runtime-portability.md#41-noto-sans-kr-경로-실행-환경별-확인)를 따릅니다.
SW는 Tailwind 없이 앱 진입점에서 `import "@hm-design/sw/styles.css"`를 한 번 추가합니다. 한 화면에서는 SW 일반 모드와 뷰어 모드 중 하나를 고르고, 글꼴 등 토큰은 가장 바깥 `<SWTheme style>`에서 바꿉니다([SW 사용 안내](packages/sw/ai/AI_USAGE.md)). SW 기본 글꼴은 Noto Sans KR이며 Figma의 Dotum을 기본값으로 쓸지는 디자인 승인 대기입니다. OrgChart는 Core CSS 두 개(`@hm-design/org-chart/engine.css`, `/style.css`)의 URL을 `stylesheetUrls`로 전달합니다(Shadow DOM, [OrgChart React README](packages/org-chart-react/README.md)).
화면 생성 Reference: [Navigation 선택 근거](docs/guidelines/navigation-single-mode.md#선택-근거), [GNB Desktop·Mobile 구성](docs/patterns/gnb.md#8-desktop--mobile)과 [내부 Sidebar](docs/patterns/gnb.md#15-gnb-only-sidebar-composition-2026-10-08), [FilterBar 조회·초기화 소비 예제](docs/patterns/filter-bar.md#61-조회-버튼으로-적용하는-소비-예제). 일반 업무 목록의 모드를 임의 확정하거나 FilterBar에 조회 API가 있다고 가정하지 않습니다.
루트 `package.json`의 `workspaces`(`packages/*`)로 연결됩니다. 확인: `npm run typecheck`, `npm run build`. 두 패키지는 `private: false`이며 ESM JS·타입 선언·CSS를 `dist`에서 export합니다. `npm run build:packages`로 재빌드할 수 있고, Registry publish 이력은 Git 기반 테스트 공유의 검증 범위에 포함하지 않습니다.
루트 `package.json`의 `workspaces`(`packages/*`)로 연결됩니다. 확인: `npm run typecheck`, `npm run build`. 위 [패키지 구성](#패키지-구성)의 5개 패키지는 모두 private이 아니며 ESM JS·타입 선언·CSS를 `dist`에서 export합니다. `npm run build:packages`로 재빌드할 수 있고, Registry publish 이력은 Git 기반 테스트 공유의 검증 범위에 포함하지 않습니다.
## 파일 위치
@@ -120,6 +139,11 @@ import { DataTable, FilterBar, useDataTable } from "@hm-design/ui" // Patterns:
- `packages/ui/src/theme/primitives.css`, `packages/ui/src/theme/default.css`: Default JSON에서 생성한 package fallback과 Semantic/shadcn 연결
- `packages/ui/src/theme/<component>.css`: 컴포넌트별 Theme 매핑과 상태 스타일
- `packages/ui/src/theme.css`: Theme CSS 진입점, `packages/ui/src/tailwind.css`: Tailwind 쪽 설정
- `packages/sw/` (`@hm-design/sw`): SW 일반 모드·뷰어 모드 컴포넌트(`src/components/`), SW Theme CSS(`src/theme/`), 패키지 테스트(`tests/`), 소비 AI 지침(`ai/`)
- `src/sw/`: SW Storybook(`SW` 그룹), `docs/sw/`: SW 설계·Figma 매핑·검증 기록
- `packages/org-chart/` (`@hm-design/org-chart`), `packages/org-chart-react/` (`@hm-design/org-chart-react`): OrgChart Core와 React Wrapper
- `src/org-chart/`: OrgChart Storybook(`OrgChart` 그룹), `docs/org-chart/`: 디자인 승인·AI 생성 정책·구현 보고
- `docs/ai/`: 패키지 `ai/` 공통 지침 원본(`npm run sync:ai-docs`, `npm run check:ai-docs`)
- `packages/center-graphic/` (`@hm-design/center-graphic`): CENTER 그래픽(제목·정보·노선/공간·시설물), `@hm-design/ui`에 의존하지 않음
- `src/center/`: CENTER Storybook (`Center Graphic` 그룹)
- `docs/center-graphic/`: CENTER 구현 기준 문서
@@ -134,7 +158,8 @@ import { DataTable, FilterBar, useDataTable } from "@hm-design/ui" // Patterns:
- `docs/components/`: 컴포넌트별 현재 구현 기준 문서
- `public/fonts/`: 기존 자료의 Noto Sans KR 글꼴과 라이선스
- `docs/guidelines/`: 프로젝트 지침·구현 원칙·Theme Token·적용/배포 가이드
- 로컬 검증·감사·진단 자료는 Git에서 제외합니다. 과거 증거 경로는 로컬 보관 기록이며 Clone에서 열 수 있는 링크가 아닙니다.
- 로컬 검증·감사·진단 자료는 Git에서 제외하는 것이 원칙입니다(`test-results/`, `_local/`, `*.log` 등 `.gitignore`). 과거 증거 경로는 로컬 보관 기록이며 Clone에서 열 수 있는 링크가 아닙니다.
- **현재 상태와의 불일치(2026-10-20 기준, 정리 정책 미결정):** `docs/sw/*-qa*/` 등에는 SW 작업의 QA 증거(스크린샷·JSON, 약 600개 파일·40MB)가 Git에 포함되어 있습니다. 반대로 `docs/org-chart/` 보고서 일부는 Git에서 제외된 `test-results/` 증거를 링크합니다(`node scripts/check-doc-links.mjs`의 missing path). 두 경우 모두 기록 보존을 위해 삭제·링크 제외 없이 유지하며, 보관 위치는 별도로 결정합니다.
## 참고 범위
@@ -155,6 +180,9 @@ Component Mapping은 Semantic/Theme palette를 참조하며 기본 API로 HM 스
- Navigation: [단일 모드 정책](docs/guidelines/navigation-single-mode.md)
- Dashboard: [Dashboard Pattern](docs/patterns/dashboard-layout.md)
- CENTER: [CENTER 구현·사용 범위](docs/center-graphic/README.md)
- SW: [SW 패키지 README](packages/sw/README.md), [SW AI 사용 안내](packages/sw/ai/AI_USAGE.md)
- OrgChart: [최종 디자인 승인 기록](docs/org-chart/design-approval.md), [AI 조직도 자동 생성 정책](docs/org-chart/ai-page-generation-rules.md)
- 외부 프로젝트 설치: [Git Submodule + npm Workspace](docs/guidelines/external-install-submodule.md)
- 프로젝트·서비스 적용: [프로젝트 지침](docs/guidelines/프로젝트-지침.md), [개발 적용](docs/guidelines/개발-적용-가이드.md)
## Package Registry / Publish (별도 내부 운영 정보)
+2
View File
@@ -268,6 +268,8 @@ AI 코딩 도구는 보통 **프로젝트 루트의** `AGENTS.md`(Codex 등)·`C
자동으로 되지 않는 것: 패키지 설치 시 루트 파일 생성(`postinstall` 없음), 도구가 `node_modules`를 스스로 탐색하는 것. 위 연결이 없으면 사용자가 "node_modules/@hm-design/…/ai/AI_GUIDE.md를 먼저 읽어"라고 요청해야 합니다.
**Git Submodule + npm Workspace로 연결한 경우**(Registry 없이 HMDesign 저장소를 예: `HMDesign/` 폴더에 Submodule로 고정하고 `workspaces`에 `HMDesign/packages/<패키지>`를 등록한 구성): `node_modules/@hm-design/<패키지>`는 `HMDesign/packages/<패키지>`로 연결되므로 지침 위치는 위 표와 같습니다(`HMDesign/packages/<패키지>/ai/`와 같은 파일). 공개 진입점은 `dist/`를 가리키므로 HMDesign 폴더 안에서 `npm ci`와 `npm run build:packages`를 먼저 실행해야 하며, `dist/`가 없으면 Import 오류를 컴포넌트 결함으로 보고하지 말고 빌드 필요로 보고합니다. HMDesign 저장소 루트의 `AGENTS.md`·`CLAUDE.md`는 HMDesign 개발용이므로 소비 프로젝트 지침으로 쓰지 않습니다.
## 10. 문서 갱신 (HMDesign 저장소 유지보수자)
컴포넌트를 추가·수정하면 같은 변경에서 해당 패키지 `ai/COMPONENT_CATALOG.md`와 필요한 `DESIGN_RULES.md`·`COMPOSITION_PATTERNS.md`를 갱신하고 `npm run check:ai-docs`를 통과시킵니다. 절차는 저장소 `docs/ai/MAINTAINING.md`(패키지 미포함)에 있습니다.
@@ -0,0 +1,101 @@
# 외부 프로젝트 설치 — Git Submodule + npm Workspace
> 2026-10-20 작성. 현재 HMDesign을 다른 React 프로젝트에서 쓰는 공식 방식입니다. npm Registry publish 없이 Git 저장소를 Submodule로 연결하고, HMDesign 안에서 패키지를 빌드한 뒤 소비 프로젝트의 npm Workspace로 연결합니다.
> 실제 적용 근거: 외부 DWG/DXF Viewer(React 19, `@hm-design/ui` + `@hm-design/sw`)가 이 절차로 연결되었습니다. 패키지 tarball 소비 검증은 `packages/sw/tests/integrated-consumer.mjs`가 별도로 수행합니다.
## 1. 전제
| 항목 | 기준 |
|---|---|
| Node.js / npm | `package-lock.json`의 엔진 조건 `^20.19.0 \|\| >=22.12.0`(Node 21 제외). 검증 기록: Node 20.20.1 / npm 10.8.2(2026-10-11), Node 22.23.2 / npm 10.9.8(2026-10-20) |
| React | 소비 프로젝트 `react`·`react-dom` `^19.0.0`(모든 패키지의 peer) |
| 네트워크 | 최초 설치에 Gitea(`https://gitea.hmac.kr/b25027/HMDesign.git`)와 npm Registry 접근 필요 |
| Git | 패키지 `dist/`는 Git에 포함되지 않습니다(`.gitignore`). Clone·Commit 변경 후에는 항상 HMDesign 안에서 다시 빌드합니다. |
## 2. Submodule 연결과 검증 Commit 고정
소비 프로젝트 루트에서 실행합니다. `<검증 Commit>`은 사용할 HMDesign Commit의 전체 SHA입니다(브랜치 이름을 그대로 쓰지 않습니다).
```bash
git submodule add https://gitea.hmac.kr/b25027/HMDesign.git HMDesign
git -C HMDesign fetch origin
git -C HMDesign checkout <검증 Commit>
git add .gitmodules HMDesign
git commit -m "chore: pin HMDesign <검증 Commit>"
```
- 소비 프로젝트에 기록되는 것은 Submodule Commit 포인터입니다. 다른 사람·CI는 `git submodule update --init --recursive`로 같은 Commit을 받습니다.
- Submodule 안에서 HMDesign 파일을 직접 수정하지 않습니다. `git status`에 `HMDesign (modified content)` / `-dirty`가 보이면 고정 Commit과 다른 코드로 빌드된 상태입니다. 그 상태의 검증 결과는 해당 Commit의 결과로 보고하지 않습니다.
## 3. HMDesign 안에서 설치·빌드
```bash
git submodule update --init --recursive
cd HMDesign
npm ci
npm run build:packages
cd ..
```
- `npm ci`는 HMDesign 자체 빌드 도구(Vite·TypeScript·Tailwind 등)를 `HMDesign/node_modules`에 설치합니다. 패키지 빌드 스크립트가 이 위치의 도구를 사용하므로 생략할 수 없습니다.
- `build:packages`는 `org-chart → ui → center-graphic → org-chart-react → sw` 순서로 5개 패키지의 `dist/`를 만듭니다. 필요한 패키지만 빌드할 때는 `npm run build:ui`, `npm run build:sw`를 쓸 수 있고, `org-chart-react`는 `org-chart`를 먼저 빌드해야 합니다.
## 4. 소비 프로젝트 Workspace 연결
소비 프로젝트 `package.json`에 사용할 패키지만 Workspace와 dependency로 추가합니다.
```json
{
"workspaces": ["HMDesign/packages/ui", "HMDesign/packages/sw"],
"dependencies": {
"@hm-design/ui": "*",
"@hm-design/sw": "*",
"react": "19.3.0",
"react-dom": "19.3.0"
}
}
```
```bash
npm install
```
- `npm install` 후 `node_modules/@hm-design/<패키지>`는 `HMDesign/packages/<패키지>`를 가리키는 링크입니다. Import는 패키지 `exports`의 공개 진입점만 사용합니다(`dist`·`src` 내부 경로 금지).
- OrgChart를 쓰면 `HMDesign/packages/org-chart`(와 React에서는 `HMDesign/packages/org-chart-react`)를 추가합니다.
- Vite 소비 프로젝트는 HMDesign 저장소 안의 예제·Export용 HTML을 의존성 스캔에서 제외하도록 `optimizeDeps.entries: ['index.html']`를 지정합니다(외부 Viewer에서 확인된 설정).
## 5. CSS Import
| 패키지 | 앱 진입점 |
|---|---|
| Web `@hm-design/ui` | CSS entry에서 `@import "tailwindcss";` → `@import "@hm-design/ui/tailwind.css";` → `@import "@hm-design/ui/theme.css";`. Tailwind 검색 범위는 [Runtime portability](./runtime-portability.md#21-tailwind-v4-소비자-검색-범위)를 따릅니다. |
| SW `@hm-design/sw` | `import "@hm-design/sw/styles.css"`를 한 번. JS import만으로 CSS가 들어오지 않습니다. 글꼴 파일은 포함하지 않습니다(기본 Noto Sans KR). |
| OrgChart | Core CSS 두 개(`@hm-design/org-chart/engine.css`, `@hm-design/org-chart/style.css`)를 URL로 해석해 `stylesheetUrls`에 전달합니다(Shadow DOM). 문서에 import하는 것만으로는 적용되지 않습니다. |
Web과 SW를 함께 쓰면 두 CSS의 순서는 서로의 계산 스타일을 바꾸지 않습니다(`integrated-consumer` 검증 항목).
## 6. AI 지침 탐색
- 소비 프로젝트의 AI 코딩 도구는 `node_modules/@hm-design/<패키지>/ai/`(= `HMDesign/packages/<패키지>/ai/`)를 읽습니다. 진입점은 각 폴더의 `AI_GUIDE.md`이며 사용 유형(Web / SW 일반·뷰어 / OrgChart / 혼합)을 먼저 정합니다.
- 루트 `AGENTS.md`·`CLAUDE.md`는 자동으로 연결되지 않습니다. `ai/AGENTS_SNIPPET.md`의 문구를 소비 프로젝트 루트 지침에 붙입니다(방법은 `AI_GUIDE.md`의 "소비 프로젝트 설정").
- HMDesign 저장소 루트의 `AGENTS.md`·`CLAUDE.md`는 HMDesign을 개발하는 AI용이며 소비 프로젝트 지침이 아닙니다.
## 7. HMDesign 버전 갱신
```bash
git -C HMDesign fetch origin
git -C HMDesign checkout <새 검증 Commit>
cd HMDesign && npm ci && npm run build:packages && cd ..
npm install
git add HMDesign
git commit -m "chore: update HMDesign to <새 검증 Commit>"
```
갱신 후 소비 프로젝트의 Typecheck·Build·화면 확인을 다시 실행합니다.
## 8. Registry 없이 사용할 때의 제약
- `dist/`가 Git에 없으므로 Clone·Commit 변경·CI마다 HMDesign 빌드(§3)가 필요합니다. CI에서도 Submodule 초기화와 `npm ci && npm run build:packages`를 실행합니다.
- HMDesign 빌드 도구와 소비 프로젝트 도구가 따로 설치됩니다(예: 외부 Viewer는 TypeScript 5.9·Vite 5, HMDesign은 TypeScript 7·Vite 8). 패키지는 HMDesign 도구로 빌드되고 소비 프로젝트는 결과물만 사용합니다.
- 버전 식별은 Submodule Commit SHA입니다. SW 패키지는 `dist/build-info.json`에 빌드한 Commit과 dirty 여부를 기록합니다.
- npm Registry publish는 현재 필수가 아닙니다. Registry 배포를 도입하면 이 문서와 별도로 [운영/배포 가이드](./운영-배포-가이드.md)에 절차를 둡니다.
@@ -75,5 +75,5 @@
| [integrated-visual.browser.mjs](../../packages/sw/tests/integrated-visual.browser.mjs) | 28/28 픽셀 동일 |
| [build-reproducibility.mjs](../../packages/sw/tests/build-reproducibility.mjs) | PASS: LF 2경로 × 2회 dist·Tarball 동일(`e98cc5ed…`), CRLF 복사본 dist 동일, 비-LF 파일 0 |
| storybook-unused-removal / overview(기존 회귀) | 처음 실행 78/81 · 51/54: 6건 중 4건은 이 작업이 의도적으로 바꾼 범위 단언(Story 수 증가, SW 소스·AI 문서), 2건은 변경 전 기준선에서도 실패하던 overview 과거 단언. 4건은 검사 의미를 유지해 범위를 갱신(SW/Header 10개·다른 항목 동일, 추가는 승인된 5개 Story만, 다른 패키지·설정·자산 무변경, SW 변경은 통합 개선 파일 목록 안에서만) → **81/81 · 52/54**. Options 메뉴를 `SW/Header`로 통합한 뒤(Story 2개 렌더 검사 추가) **83/83 · 52/54** |
| overview 남은 실패 2건 — **기존 실패, 신규 결함 아님** | "24 retained Story IDs…", "retained non-Overview declarations byte-identical…": 이미 은퇴한 Dropdown Story 시점(24개, 기준 Commit `50bf5bd`)을 고정 비교하는 과거 단언입니다. 이 작업을 시작하기 전 기준선(HEAD `5faa1f1`)에서도 같은 2건이 실패했습니다([baseline-overview.log](./integrated-improvement-qa/baseline-overview.log)). 현재 인벤토리는 storybook-unused-removal이 검증하며, overview 기능 검사 52건은 모두 PASS |
| overview 남은 실패 2건 — **기존 실패, 신규 결함 아님** | "24 retained Story IDs…", "retained non-Overview declarations byte-identical…": 이미 은퇴한 Dropdown Story 시점(24개, 기준 Commit `50bf5bd`)을 고정 비교하는 과거 단언입니다. 이 작업을 시작하기 전 기준선(HEAD `5faa1f1`)에서도 같은 2건이 실패했습니다([baseline-overview/chrome-result.json](./integrated-improvement-qa/baseline-overview/chrome-result.json) `checks`의 FAIL 2건, 실행 로그 `baseline-overview.log`는 Git 제외(`*.log`) 로컬 기록). 현재 인벤토리는 storybook-unused-removal이 검증하며, overview 기능 검사 52건은 모두 PASS |
| font-weight 정책 테스트 | 3/3 PASS |
+2
View File
@@ -268,6 +268,8 @@ AI 코딩 도구는 보통 **프로젝트 루트의** `AGENTS.md`(Codex 등)·`C
자동으로 되지 않는 것: 패키지 설치 시 루트 파일 생성(`postinstall` 없음), 도구가 `node_modules`를 스스로 탐색하는 것. 위 연결이 없으면 사용자가 "node_modules/@hm-design/…/ai/AI_GUIDE.md를 먼저 읽어"라고 요청해야 합니다.
**Git Submodule + npm Workspace로 연결한 경우**(Registry 없이 HMDesign 저장소를 예: `HMDesign/` 폴더에 Submodule로 고정하고 `workspaces`에 `HMDesign/packages/<패키지>`를 등록한 구성): `node_modules/@hm-design/<패키지>`는 `HMDesign/packages/<패키지>`로 연결되므로 지침 위치는 위 표와 같습니다(`HMDesign/packages/<패키지>/ai/`와 같은 파일). 공개 진입점은 `dist/`를 가리키므로 HMDesign 폴더 안에서 `npm ci`와 `npm run build:packages`를 먼저 실행해야 하며, `dist/`가 없으면 Import 오류를 컴포넌트 결함으로 보고하지 말고 빌드 필요로 보고합니다. HMDesign 저장소 루트의 `AGENTS.md`·`CLAUDE.md`는 HMDesign 개발용이므로 소비 프로젝트 지침으로 쓰지 않습니다.
## 10. 문서 갱신 (HMDesign 저장소 유지보수자)
컴포넌트를 추가·수정하면 같은 변경에서 해당 패키지 `ai/COMPONENT_CATALOG.md`와 필요한 `DESIGN_RULES.md`·`COMPOSITION_PATTERNS.md`를 갱신하고 `npm run check:ai-docs`를 통과시킵니다. 절차는 저장소 `docs/ai/MAINTAINING.md`(패키지 미포함)에 있습니다.
+2
View File
@@ -268,6 +268,8 @@ AI 코딩 도구는 보통 **프로젝트 루트의** `AGENTS.md`(Codex 등)·`C
자동으로 되지 않는 것: 패키지 설치 시 루트 파일 생성(`postinstall` 없음), 도구가 `node_modules`를 스스로 탐색하는 것. 위 연결이 없으면 사용자가 "node_modules/@hm-design/…/ai/AI_GUIDE.md를 먼저 읽어"라고 요청해야 합니다.
**Git Submodule + npm Workspace로 연결한 경우**(Registry 없이 HMDesign 저장소를 예: `HMDesign/` 폴더에 Submodule로 고정하고 `workspaces`에 `HMDesign/packages/<패키지>`를 등록한 구성): `node_modules/@hm-design/<패키지>`는 `HMDesign/packages/<패키지>`로 연결되므로 지침 위치는 위 표와 같습니다(`HMDesign/packages/<패키지>/ai/`와 같은 파일). 공개 진입점은 `dist/`를 가리키므로 HMDesign 폴더 안에서 `npm ci`와 `npm run build:packages`를 먼저 실행해야 하며, `dist/`가 없으면 Import 오류를 컴포넌트 결함으로 보고하지 말고 빌드 필요로 보고합니다. HMDesign 저장소 루트의 `AGENTS.md`·`CLAUDE.md`는 HMDesign 개발용이므로 소비 프로젝트 지침으로 쓰지 않습니다.
## 10. 문서 갱신 (HMDesign 저장소 유지보수자)
컴포넌트를 추가·수정하면 같은 변경에서 해당 패키지 `ai/COMPONENT_CATALOG.md`와 필요한 `DESIGN_RULES.md`·`COMPOSITION_PATTERNS.md`를 갱신하고 `npm run check:ai-docs`를 통과시킵니다. 절차는 저장소 `docs/ai/MAINTAINING.md`(패키지 미포함)에 있습니다.
+2
View File
@@ -268,6 +268,8 @@ AI 코딩 도구는 보통 **프로젝트 루트의** `AGENTS.md`(Codex 등)·`C
자동으로 되지 않는 것: 패키지 설치 시 루트 파일 생성(`postinstall` 없음), 도구가 `node_modules`를 스스로 탐색하는 것. 위 연결이 없으면 사용자가 "node_modules/@hm-design/…/ai/AI_GUIDE.md를 먼저 읽어"라고 요청해야 합니다.
**Git Submodule + npm Workspace로 연결한 경우**(Registry 없이 HMDesign 저장소를 예: `HMDesign/` 폴더에 Submodule로 고정하고 `workspaces`에 `HMDesign/packages/<패키지>`를 등록한 구성): `node_modules/@hm-design/<패키지>`는 `HMDesign/packages/<패키지>`로 연결되므로 지침 위치는 위 표와 같습니다(`HMDesign/packages/<패키지>/ai/`와 같은 파일). 공개 진입점은 `dist/`를 가리키므로 HMDesign 폴더 안에서 `npm ci`와 `npm run build:packages`를 먼저 실행해야 하며, `dist/`가 없으면 Import 오류를 컴포넌트 결함으로 보고하지 말고 빌드 필요로 보고합니다. HMDesign 저장소 루트의 `AGENTS.md`·`CLAUDE.md`는 HMDesign 개발용이므로 소비 프로젝트 지침으로 쓰지 않습니다.
## 10. 문서 갱신 (HMDesign 저장소 유지보수자)
컴포넌트를 추가·수정하면 같은 변경에서 해당 패키지 `ai/COMPONENT_CATALOG.md`와 필요한 `DESIGN_RULES.md`·`COMPOSITION_PATTERNS.md`를 갱신하고 `npm run check:ai-docs`를 통과시킵니다. 절차는 저장소 `docs/ai/MAINTAINING.md`(패키지 미포함)에 있습니다.
+34 -11
View File
@@ -5,7 +5,8 @@
// 2) Regression: every existing SW Story renders identically with the new cascade and with the legacy cascade
// (token blocks re-declared on every `.hm-sw-header, .hm-sw-theme`, i.e. the code before this change): computed
// font / color / geometry of every SW element and an element screenshot comparison. No Story sets an override,
// so any difference would be a regression.
// so any difference would be a regression. Pixels are compared with a control shot taken after the CURRENT token
// blocks are injected once more (2026-10-20): re-declaring them alone re-rasterizes a few gradient pixels.
// Usage: node packages/sw/tests/theme-inheritance.browser.mjs <storybook-url> <out-dir>
import { createRequire } from 'node:module'
import { mkdir, readFile, writeFile } from 'node:fs/promises'
@@ -137,13 +138,19 @@ const fmt = map => Object.entries(map).map(([family, count]) => `${count}× ${fa
// ---------- 2. Regression: new cascade vs legacy cascade on every existing SW Story ----------
const here = path.dirname(fileURLToPath(import.meta.url))
const legacy = (await Promise.all(['sw.css', 'sw-viewer.css', 'sw-footer.css'].map(file => readFile(path.join(here, '../src/theme', file), 'utf8'))))
const outermost = ':is(.hm-sw-header, .hm-sw-theme):not(:where(.hm-sw-header, .hm-sw-theme) *)'
const tokenBlocks = (await Promise.all(['sw.css', 'sw-viewer.css', 'sw-footer.css'].map(file => readFile(path.join(here, '../src/theme', file), 'utf8'))))
.map(text => {
const start = text.indexOf(':is(.hm-sw-header, .hm-sw-theme):not(:where(.hm-sw-header, .hm-sw-theme) *) {')
const start = text.indexOf(`${outermost} {`)
const end = text.indexOf('\n}', start)
if (start < 0 || end < 0) throw new Error('token block not found')
return `.hm-sw-header,\n.hm-sw-theme ${text.slice(text.indexOf('{', start), end + 2)}`
}).join('\n')
return text.slice(text.indexOf('{', start), end + 2)
})
const legacy = tokenBlocks.map(block => `.hm-sw-header,\n.hm-sw-theme ${block}`).join('\n')
// Control (2026-10-20): the SAME token blocks on the SAME selector, i.e. the current cascade declared once more.
// Re-declaring the token blocks re-rasterizes a few gradient pixels (±1–2 per channel at the end of the Footer
// command row) even though no value changes, so the legacy shot is also compared with this control shot.
const control = tokenBlocks.map(block => `${outermost} ${block}`).join('\n')
const index = await (await fetch(`${storybook}/index.json`)).json()
const ids = Object.values(index.entries).filter(entry => entry.type === 'story' && /^sw-(header|popup|viewer|footer|home-menu)/.test(entry.id)).map(entry => entry.id)
const snapshot = page => page.evaluate(() => [...document.querySelectorAll('.hm-sw-header, .hm-sw-theme')].flatMap(root => [root, ...root.querySelectorAll('*')]).map(element => {
@@ -161,21 +168,37 @@ for (const width of [1920, 1440, 1180, 900, 560]) for (const id of ids) {
await page.waitForTimeout(150)
// Pixels that change between two shots of the SAME CSS (live Story content) are masked out as noise.
const shotA2 = await shot()
// Control: the current cascade declared again. Its computed values must equal the first state.
await page.addStyleTag({ content: control })
await page.waitForTimeout(150)
const c = await snapshot(page)
const shotC = await shot()
await page.waitForTimeout(150)
const shotC2 = await shot()
await page.addStyleTag({ content: legacy })
await page.waitForTimeout(150)
const b = await snapshot(page)
const shotB = await shot()
const changed = a.length === b.length ? a.filter((value, i) => value !== b[i]).length : -1
let pixels = -1, noise = 0
if (shotA.width === shotB.width && shotA.height === shotB.height && shotA2.height === shotA.height) {
const changedValues = (x, y) => x.length === y.length ? x.filter((value, i) => value !== y[i]).length : -1
const changed = changedValues(a, b), controlChanged = changedValues(a, c)
let pixels = -1, noise = 0, controlPixels = -1
if ([shotA2, shotC, shotC2, shotB].every(item => item.width === shotA.width && item.height === shotA.height)) {
pixels = 0
controlPixels = 0
for (let i = 0; i < shotA.data.length; i += 4) {
const same = (x, y) => x[i] === y[i] && x[i + 1] === y[i + 1] && x[i + 2] === y[i + 2] && x[i + 3] === y[i + 3]
if (!same(shotA.data, shotA2.data)) noise++
else if (!same(shotA.data, shotB.data)) pixels++
if (!same(shotA.data, shotC.data)) controlPixels++
// Live Story content changing at rest (before or after the control) is masked out as noise.
if (!same(shotA.data, shotA2.data) || !same(shotC.data, shotC2.data)) noise++
// First shot and control shot both render the unchanged cascade (re-raster artifacts appear only in the
// control; a transient relayout may appear in either one). The legacy cascade fails when its pixel matches
// neither of them exactly.
else if (!same(shotC.data, shotB.data) && !same(shotA.data, shotB.data)) pixels++
}
}
regression.push({ id, width, elements: a.length, changedElements: changed, diffPixels: pixels, maskedNoisePixels: noise, result: changed === 0 && pixels === 0 ? 'PASS' : 'FAIL' })
// Computed values: legacy vs first state (and control vs first state). Pixels: legacy vs first state and control.
regression.push({ id, width, elements: a.length, changedElements: changed, controlChangedElements: controlChanged, diffPixels: pixels,
controlRasterPixels: controlPixels, maskedNoisePixels: noise, result: changed === 0 && controlChanged === 0 && pixels === 0 ? 'PASS' : 'FAIL' })
await page.close()
}
// Negative control: the legacy cascade must reproduce the reported defect on the themed Story (the comparison above is
+2
View File
@@ -268,6 +268,8 @@ AI 코딩 도구는 보통 **프로젝트 루트의** `AGENTS.md`(Codex 등)·`C
자동으로 되지 않는 것: 패키지 설치 시 루트 파일 생성(`postinstall` 없음), 도구가 `node_modules`를 스스로 탐색하는 것. 위 연결이 없으면 사용자가 "node_modules/@hm-design/…/ai/AI_GUIDE.md를 먼저 읽어"라고 요청해야 합니다.
**Git Submodule + npm Workspace로 연결한 경우**(Registry 없이 HMDesign 저장소를 예: `HMDesign/` 폴더에 Submodule로 고정하고 `workspaces`에 `HMDesign/packages/<패키지>`를 등록한 구성): `node_modules/@hm-design/<패키지>`는 `HMDesign/packages/<패키지>`로 연결되므로 지침 위치는 위 표와 같습니다(`HMDesign/packages/<패키지>/ai/`와 같은 파일). 공개 진입점은 `dist/`를 가리키므로 HMDesign 폴더 안에서 `npm ci`와 `npm run build:packages`를 먼저 실행해야 하며, `dist/`가 없으면 Import 오류를 컴포넌트 결함으로 보고하지 말고 빌드 필요로 보고합니다. HMDesign 저장소 루트의 `AGENTS.md`·`CLAUDE.md`는 HMDesign 개발용이므로 소비 프로젝트 지침으로 쓰지 않습니다.
## 10. 문서 갱신 (HMDesign 저장소 유지보수자)
컴포넌트를 추가·수정하면 같은 변경에서 해당 패키지 `ai/COMPONENT_CATALOG.md`와 필요한 `DESIGN_RULES.md`·`COMPOSITION_PATTERNS.md`를 갱신하고 `npm run check:ai-docs`를 통과시킵니다. 절차는 저장소 `docs/ai/MAINTAINING.md`(패키지 미포함)에 있습니다.