32 KiB
AI Generation Guard — 경량 검증
별도 요구사항 액션 검사
node scripts/guard-requirements.mjs <guard.json> <requirements-actions.json>는 원본 Guard의 status/issues를 보존하고 별도 판정을 출력한다. 정책 입력 예시는 {"schemaVersion":1,"actionPolicy":{"allowedLabels":["조회","초기화"]}}다. 실제 요구사항에서 허용한 소비 화면의 Button/button/link 이름만 작성하며, Component 기본 동작을 검사하는 경우 그 이름도 명시한다. 허용하지 않은 정적 이름(예: 미요청 CSV 다운로드)은 REQ-003 FAIL, 동적/복합 이름·spread props·오래된 source/contract는 NOT VERIFIED다.
자유 형식 요구사항의 의미를 자동 추정하지 않는다. 범위는 FULL Guard가 확인한 파일의 literal 액션 이름이며 wrapper/namespace/동적 UI/API 호출과 전체 접근성은 별도 검토한다. 이 검사의 PASS로 원본 CSS intent·Runtime 미검증을 제거하지 않는다. 이전 실험에는 소급 적용해 원래 점수를 바꾸지 않는다.
정적 결과와 최종 화면 결과
JSON schemaVersion 2는 기존 status, exit code, 원본 issues를 유지하고 staticStatus, runtimeStatus, 그룹별 basis/evidence, Token 참조와 source/contract fingerprint를 추가한다. staticStatus는 RENDER 항목만 분리하며 CSS intent·native control·해석 불가 graph는 그대로 미검증이다. runtimeStatus는 Guard 자체에서 항상 NOT VERIFIED다. 따라서 RENDER-002만 남은 화면의 정적 PASS와 미해석 화면을 구분할 수 있다. 소비 graph는 변경하지 않는다.
Theme Token 검사
현재 확장 규칙은 아래 CSS Intent와 동적 Token 검사를 함께 적용한다. 2026-10-11 최신 자동 회귀는 Guard 94개·증거 연결 20개·요구사항 연결 5개, 합계 119/119 PASS다(최종 Baseline 근거). 과거 75개는 CSS Intent·동적 Token 확장 당시 scripts/ai-generation-guard.test.mjs의 테스트 사례 수이며 Guard 규칙 수나 Runtime Theme 입력 수가 아니다. 아래 63개/11개 및 51개/46개 테스트 수와 안정화 설명도 당시 기록이다. 이후 수량은 세 테스트 파일의 실제 사례와 실행 로그의 tests/pass/fail/skipped를 대조한다. 자동 회귀 PASS는 Guard Runtime·소비 CSS Design Intent의 NOT VERIFIED를 해소하지 않는다.
TOKEN-001: CSS 선언 값, JSX style 문자열, Tailwind 임의 값·v4 shorthand의 var(--hm-...) 참조가 연결된 공개 Theme/Status CSS 또는 소비 CSS/inline custom property 선언에 없으면 FAIL. 사용되지 않은 파일이나 다른 lazy Story의 선언은 빌리지 않는다. fallback 색이 있어도 없는 HM token은 FAIL이다. 파싱 불가 public CSS 정의는 NOT VERIFIED. 계산된 HM 참조는 유한 후보 전체를 확인하고, 해석 불가 참조는 TOKEN-002 NOT VERIFIED다. selector별 cascade·실제 computed value는 브라우저 확인 대상이다. HM 이외 변수도 수집하지만, 연결 선언이 없는 외부 host 변수는 HM 미정의 FAIL과 구분하여 NOT VERIFIED로 남긴다.
tokenEvidence는 entry별 연결 정의 수와 검사한 참조를 제공한다. 공개 Token의 정답은 실제 연결 CSS 및 Runtime 계약표다. Component mapping은 runtime JSON key와 같은 집계가 아니다. 별도 토큰·색상은 추가하지 않는다.
현재 Runtime Theme 공개 입력은 contract.ts의 runtimeThemeContract 최상위 Key 94개다. type: "color" 92개는 Default Theme JSON의 tokens Key 92개와 대응하며, type: "radius" 2개는 package에서 관리한다. 계약 변경 시 이 객체의 Key·type별 개수와 JSON tokens Key 집합을 대조하여 재집계한다. Guard는 실제 bootstrap에 연결된 CSS 선언과 사용 참조를 검사하므로 검사 범위를 Runtime 입력 94개로 제한하지 않는다. Guard 회귀 사례 94개와 Runtime 입력 94개는 집계 대상이 달라 서로 연동되는 수량이 아니다.
node scripts/public-theme-tokens.mjs로 연결된 공개 CSS의 선언 목록을 JSON으로 출력한다. 기준 Commit 3ebc84e32cf91298a2a4d8046753244c499fd719에서 1,203개 고유 --hm-* 선언명이며 Runtime JSON 입력 Key 94개와 다르다. 집계 범위는 @hm-design/ui/theme.css + @hm-design/ui/dashboard-status.css의 연결 CSS이고 definitionCount / incomplete: false를 확인한다. selector마다 반복된 선언의 총 행 수나 Guard 규칙 수가 아니며 CSS가 바뀌면 위 명령으로 재집계한다.
보존된 검증은 기존 전체 범위 FAIL 0 / NOT VERIFIED 16건과 최신 변경 범위 FAIL 0 / NOT VERIFIED 12건으로 서로 다른 scope·source fingerprint의 기록이다. 둘을 합치거나 전체 PASS로 표현하지 않는다. 원본 CSS styleEvidence는 STATIC PASS / RUNTIME NOT VERIFIED / DESIGN_INTENT PASS / FINAL NOT VERIFIED이며 결합 styleVerification의 NOT VERIFIED도 유지한다. Native role widget은 REUSE-006으로 근거 검토를 요구하고 일반 콘텐츠 HTML은 허용한다.
Runtime 증거 연결
원본 Guard JSON과 실제 브라우저 증거를 별도로 보존한다. Runtime JSON에 sourceFingerprint, contractFingerprint, 네 viewports(width/status), 이름 있는 핵심 interactions(name/status)를 기록한 뒤 실행한다.
node scripts/guard-evidence.mjs guard.json runtime.json
결합 결과는 정적 PASS + 일치하는 해시 + 네 viewport PASS + 핵심 동작 PASS일 때만 PASS다. 정적 FAIL, native/CSS intent의 미검증은 제거하지 않는다. 소스·공개 계약 변경 시 이전 증거는 NOT VERIFIED다. Runtime JSON은 제출된 검사 증거이며 도구가 독립 인증하거나 모든 상태를 보장하는 것은 아니다. guard-evidence.test.mjs가 오래된 증거, 누락 viewport/동작, 정적 FAIL/미검증 보존을 검사한다. 원본 status와 결합 결과를 혼용하지 않는다.
--result의 React bootstrap 증명이 없거나 RENDER-001이면 결합 PASS를 허용하지 않는다. 결합 결과도 원본 FULL_SCREEN/PARTIAL scope를 그대로 상속한다. 당시 확장은 Guard 51+12=63개, 증거 연결 5+6=11개였다. 이후 CSS Intent·동적 Token 확장 당시 Guard 75개·증거 연결 20개였으며, 요구사항 연결 5개는 별도 suite다. 최신 자동 회귀 수량은 위 Theme Token 검사 절을 따른다. 과거 결과는 당시 기록으로 보존한다.
재현 가능한 예제: examples/ai-quality의 10개 독립 bootstrap. node scripts/verify-quality-runtime.mjs는 각 실제 dist 화면을 검사하고 Guard·Runtime·결합 JSON과 캡처를 _local/quality-20261010에 각각 기록한다. Playwright 경로는 HM_PLAYWRIGHT_DIR, Chrome 실행 경로는 HM_CHROME_PATH, 서버 URL은 HM_QUALITY_URL로 명시한다. 실패 범위 재검사는 인자로 화면 ID를 전달한다.
node scripts/ai-generation-guard.mjs --target <앱> --scope FULL_SCREEN|PARTIAL
- 기존 Navigation·Storybook 격리·CSS entry·공개 Import 검사를 유지한다. Navigation 재사용 시 확정한
--mode를 전달한다. - PARTIAL은
--files <수정 파일,...>로 지정한다. 앱 bootstrap graph만 탐색하며 별도 전체 저장소 검색은 하지 않는다. 디자인 시스템 내부 구현은 소비 화면 검사에서 제외한다. - 정상 텍스트 출력은
PASS한 줄이다. 문제는 최대 5개 파일·행·원인만 표시한다. 기계 처리·전체 진단은--json으로 확인한다. REUSE-001FAIL: 공개 이름에 대응하는 HMdata-slot을 소비자의 기본 HTML에 부여함. 공식 여부가 불명확한 wrapper는 NOT VERIFIED다. slot은 컴포넌트 재사용 증거가 아니다.REUSE-002FAIL: 공개 컴포넌트를 import한 뒤 참조하지 않음. re-export·type import·별칭·namespace·props 전달은 단순 미사용으로 판정하지 않는다.REUSE-003NOT VERIFIED: HM slot/class를 겨냥한 CSS. 승인된 override와 복제 의도를 정적 코드만으로 구분할 수 없으므로 실제 사용을 확인한다.REUSE-004NOT VERIFIED: 기본 select/input/textarea/button/label 사용. 합법적인 기본 HTML과 기존 컴포넌트 대체 구현을 구분하여 검토한다. native Label은 공개 Label의 지원 사용처인지 control 연결과 예외 이유를 확인하며 무조건 FAIL로 판정하지 않는다. 일반 div·레이아웃 CSS는 복제로 단정하지 않는다.- 최종 제출물이 있으면 같은 실행에
--result <HTML 또는 JS/TS bootstrap>을 지정한다. 해당 artifact를 시작점으로 검사한다. React mount가 확인되지 않으면RENDER-001, 확인되어도 실제 브라우저 결과는RENDER-002NOT VERIFIED다. 경로 이름만으로 preview.html을 금지하지 않는다. 실제 React를 실행하는 HTML은 정상 entry다.
PASS는 정적 계약 통과만 의미한다. 실제 React 화면을 빌드하고 브라우저에서 utility 적용·레이아웃·핵심 동작을 최소 1회 확인한다. 별도 HTML 모방 화면이나 이미지로 이를 대체하지 않는다. 화면에서 실행되지 않는 코드, 조건부 렌더, CSS 복제 의도, 동적 binding은 정적 검사만으로 완전히 판별할 수 없다. 미확인은 NOT VERIFIED로 기록한다.
기존 exit code 계약은 FAIL=1, 설정/도구 오류=2, 그 외=0이다. 따라서 exit 0만으로 PASS를 기록하지 않는다. JSON의 status, checkedGroups, issues를 함께 확인한다. NOT VERIFIED는 FAIL이 없다는 사실과 전체 계약 검증 완료를 구분한다.
FAIL 수정 후 수정 파일만 PARTIAL 재검사한다. 성공한 검사는 반복하지 않는다. 최소 회귀 테스트: node --test scripts/ai-generation-guard.test.mjs (새 의존성 설치 없음).
AI 생성 개선 회귀는 기존 46개 본문을 보존하고 native Label 검토·공개 Label 별칭·외부 React FULL/PARTIAL·실제 Sidebar/Theme 누락·graph 밖 파일을 5개 추가하여 51개다. 아래 안정화 절의 46/46은 당시 기록이다. 개선 및 재실험 결과를 함께 확인한다.
Production 분기 분석
독립 외부 React 앱
Production graph는 대상 앱의 bootstrap에서 시작한다. 본 저장소의 graph에 외부 화면이나 Story를 등록할 필요가 없다. 기존 CLI를 본 저장소에서 실행하여 계약은 현재 공개 API, 검사 대상은 외부 앱으로 분리한다.
node scripts/ai-generation-guard.mjs --target <외부 앱> --scope FULL_SCREEN --mode GNB_ONLY --entry index.html --result index.html --json
node scripts/ai-generation-guard.mjs --target <외부 앱> --scope PARTIAL --mode GNB_ONLY --files src/main.tsx --entry index.html --result index.html --json
--files는 bootstrap을 대신하지 않는다. graph 밖 파일은 SCOPE-001, 누락 bootstrap은 ENTRY-001이며 미검증이다. --result를 전달하여 실제 React mount를 찾으면 RENDER-002가 남는다. 이것은 검사 대상 누락이 아니라 별도 브라우저 확인 요구다. 승인된 소비 CSS도 REUSE-003의 원본 판정을 유지하고 별도 증거로 검토한다. 브라우저 확인 또는 CSS 승인 기록으로 원본 JSON을 PASS로 덮어쓰지 않는다. 외부 앱의 package 버전이 이 저장소 계약과 다른지는 별도로 확인한다.
Pattern 선택 적합성·native Label 지원 경계·요구사항 밖 CSV 등 액션은 요구사항/선택 기록/실제 동작을 대조하는 수동 평가 대상이다. Guard 정적 PASS만으로 이를 확인했다고 보고하지 않는다. STORYBOOK profile은 기존 Reference 검사용이며 외부 업무 화면 검사 우회 경로가 아니다.
기본 --profile PRODUCTION은 HTML/JS bootstrap의 import graph를 검사한다. Navigation은 분기 분석기가 직접 component 호출의 literal props, 기본 props, immutable local 값, JSX return/slot, ternary/logical 조건을 추적한다. 현재 entry에서 사용되지 않는 JSX 값은 실제 mount와 구분한다. 같은 컴포넌트의 호출이 여러 개이면 모든 호출의 결과를 합치며, 한 호출에서 위반이 확인되면 FAIL을 유지한다.
- 실제 도달하는 금지 Sidebar/혼합 Navigation:
NAV-001/002FAIL. - literal로 배제된 분기·반환되지 않는 Reference JSX 값: Navigation violation으로 세지 않는다. import·공개 API·CSS 검사는 계속 수행한다.
- 동적 props, spread, mutation, opaque callback, 지원하지 않는 제어 흐름: 가능한 위반은
NOT VERIFIED로 남는다. 미분석 JSX는 기존 검사를 유지하며 없다고 가정하지 않는다. DashboardLayout.sidebar가 현재 props로undefined/null/false임이 증명되면 빈 slot이다. 값이 불명확하면NAV-005를 유지한다.
분석기는 JavaScript를 실행하지 않으며 일반적인 bundler/전체 CFG가 아니다. NOT VERIFIED는 실제 entry와 데이터 상태를 브라우저에서 확인하거나, literal props를 가진 직접 bootstrap fixture로 확인한다. checkedFiles는 import graph의 파일이며 실제 mount 목록이 아니다. checkedHMElements도 정적 참조 목록이고 화면 DOM 개수의 증거가 아니다.
독립 Storybook 검사
STORYBOOK은 --profile 값이며 scope가 아니다. 지원 scope는 FULL_SCREEN | PARTIAL이고, --scope STORYBOOK은 설정 오류다. Storybook profile에는 Production 전용 --result를 전달하지 않는다.
node scripts/ai-generation-guard.mjs --target . --profile STORYBOOK --scope FULL_SCREEN --json
node scripts/ai-generation-guard.mjs --target . --profile STORYBOOK --scope PARTIAL --files src/components/tabs.stories.tsx,src/patterns/page-header.stories.tsx --json
.storybook/main.ts/js/mjs의 literal stories glob과 .storybook/preview.tsx/ts/jsx/js를 별도 root로 사용한다. 기존 workspace의 fast-glob을 사용하며 패키지 설치·설정 실행은 하지 않는다. computed discovery는 SB-004 NOT VERIFIED이며 --entry story-path,...로 범위를 명시할 수 있다. 각 Story의 의존 graph에 공통 preview CSS/decorator를 연결하며, 다른 lazy Story의 CSS를 연결 증거로 빌리지 않는다. Production graph에 Story를 추가하지 않는다. --result는 Production 전용이다.
| 검사 | Production | Storybook |
|---|---|---|
Story/helper/reference import, .sb-* CSS |
기존 격리 검사 FAIL | Reference의 정상 의존성 허용 |
| 공개 import/export·private source import | 기존 검사 유지 | 같은 검사 유지 |
| Theme/utility/status CSS entry | bootstrap 연결 | preview + Story graph 연결 |
| Navigation | 전달한 mode에 맞춰 분기 검사 | mode 미지정 시 Reference API를 보존(NOT APPLICABLE); 명시 mode는 정적 위반 검사 |
| native public component slot 위조 | FAIL | FAIL |
| 공개 컴포넌트가 없는 내부 namespace wrapper slot | 기존 FAIL/NOT VERIFIED | 문서화된 Reference composition인지 NOT VERIFIED; 재사용 증거로 인정하지 않음 |
새 업무 화면을 Story로 등록하는 것은 여전히 금지된다. Storybook profile은 신규 Production Navigation을 허용하는 경로가 아니다. Story별 대표/verification 분류, 혼합 Navigation의 승인 의도와 모든 runtime 상태는 자동 확정하지 않는다. 페이지 단위 검사는 관련 Story를 --entry로 고르고 mode를 명시하여 수행한다. 기존 Component/Pattern 구현은 소비 화면 검사에서 제외하며 패키지 typecheck/build 및 별도 runtime 검증으로 확인한다. 선택한 package source가 graph 밖이면 SCOPE-001을 유지한다.
CSS Intent 및 보존 범위
CSS 측정과 Intent 검토 근거
Runtime JSON의 선택적 cssEvidence를 guard-evidence.mjs가 별도 결과로 연결한다. 원본 Guard issues/status와 REUSE-003는 변경하지 않는다. 기존 Runtime JSON도 호환된다. 측정이 없으면 cssEvidence.status는 NOT VERIFIED이며 기존 viewport/interaction Runtime 판정과 구분한다.
| 범위 | 근거 | 한계 |
|---|---|---|
| 정적 규칙 | Guard의 공개 Component import·Navigation·CSS entry·tokenEvidence | 실제 mount/cascade/스타일 생성은 별도 확인 |
| 실제 스타일 | selector/property/width별 computed 값과 기대값의 비교 | 기록한 상태·속성만 확인; 디자인 의도 승인 아님 |
| 사용자 디자인 의도 | issue 단위 rationale과 문서 recipe SHA/행 연결 | REUSE-003 NOT VERIFIED 유지; 자동 승인 없음 |
| 동적 Token | 실제 상태에서 해석된 custom property 값 | 미실행 테마·상태와 동적 참조는 NOT VERIFIED |
{
"cssEvidence": {
"checks": [{
"name": "완료 Badge ink", "kind": "COMPUTED_STYLE", "width": 390,
"selector": ".dashboard-status [data-slot=\"badge\"][data-status=\"done\"]",
"property": "color", "actual": "rgb(255, 255, 255)", "expected": "rgb(255, 255, 255)"
}],
"intentReviews": [{
"issue": { "code": "REUSE-003", "file": "src/index.css", "line": 15 },
"rationale": "공식 Dashboard Badge recipe와 소비 mapping을 비교함. 사용자 의도는 미확정.",
"reference": { "file": "docs/patterns/dashboard-layout.md", "line": 69, "sha256": "실제 문서 파일의 SHA256" }
}]
}
}
위 값·행은 입력 형식 예시이며 해당 화면에서 실제 측정하고 현재 문서 행·해시를 기록한다. CSS_VARIABLE kind는 property가 --로 시작해야 하며 getComputedStyle(element).getPropertyValue(property).trim()으로 측정한다. COMPUTED_STYLE은 color/backgroundColor 등 실제 computed 속성을 기록한다. 기대값은 공개 CSS/공식 recipe에서 정하고 actual을 복사해 만들지 않는다. Theme import → 상태 CSS → 실제 변수 → Badge color/background/border를 각각 연결한다.
측정은 네 표준 폭 중 Runtime PASS가 있는 폭에만 연결하며 source/contract가 stale하면 사용하지 않는다. 비어 있는 값·지원하지 않는 kind/폭은 NOT VERIFIED다. 제공한 status 대신 actual/expected 일치로 판정하고 실제 mismatch는 Runtime/결합 FAIL을 유지한다. 누락·잘못된 검토 근거는 CSS 증거 NOT VERIFIED로 남긴다. 문서 참조는 Guard contractSource 안의 실제 파일과 해시가 일치하고 원본 issue의 code/file/line이 같아야 연결된다. 연결된 검토도 NOT VERIFIED로 표시한다. 측정 PASS와 결합 전체 PASS를 혼용하지 않는다.
원본 Guard의 contract 해시에 guard-evidence.mjs가 포함되므로 이 도구가 변경되면 이전 연결 Runtime을 새 계약의 PASS로 옮기지 않는다. 재사용 원본은 보존하고, 변경된 계약의 Guard와 실제 브라우저 측정을 별도 결과에 기록한다. 전체 Storybook·이미 성공한 화면 재생성은 필요하지 않다.
정상 Gradient·token·utility를 통과 목적으로 수정하지 않는다. 공식 package Theme CSS는 공개 entry로 연결하고 내부 구현은 소비자 replica 검사에서 제외한다. 소비자의 HM slot/class 스타일은 승인된 override인지 복제인지 정적 판정할 수 없으므로 REUSE-003 NOT VERIFIED를 유지한다. Tabs 내부 tabs-* slot도 이 검토 범위에 포함한다. 파싱 불가 CSS는 CSS-005 NOT VERIFIED이며 기본 HTML의 공개 slot 위조는 REUSE-001 FAIL이다.
CSS entry의 정적 PASS는 Tailwind source 검색 범위·utility 생성·computed style을 보장하지 않는다. index.css → tailwind.css, hm-theme.css → @hm-design/ui/theme.css, 필요 시 공개 status CSS, 공개 @hm-design/ui/tailwind.css의 source 경로를 실제 build/브라우저에서 확인한다. D-02 Primary 80% 상단 stop과 Secondary/Legacy 보존은 Theme source 해시·CSS 선언·실제 Tabs 렌더로 검증한다.
CSS Intent와 동적 Token 검사 (2026-10-10)
기존 Production bootstrap graph, PARTIAL 선택/연관 의존성, Storybook entry별 격리는 유지한다. 추가 분석기는 소비 graph만 읽으며 Component/Theme 구현이나 대표 Story를 수정하지 않는다. 기존 status/staticStatus/issues를 지우지 않고 styleEvidence와 결합 결과의 styleVerification을 추가한다. 새 위반은 기존 exit 1 계약을 따른다.
검사 대상은 AI 생성 화면의 스타일 규칙 준수다. 외주 퍼블리셔의 Theme 편집 기능·편집 권한·Designer 기능을 추가하거나 확대하지 않는다. 기존 Theme 적용/전환·Token 값·공개 API·Component 스타일은 유지한다. 검사 예제나 문서 recipe는 새로운 CSS override 또는 Theme 편집 방식을 승인하지 않는다. Theme 기능 자체의 개선 필요성은 별도 검토 항목으로만 보고한다.
| 영역 | 확인하는 내용 | 결과와 한계 |
|---|---|---|
| A — STATIC | 공개 CSS entry/import, 연결 Token 선언, 유한 동적 후보, 명확한 Component 하드코드 | 기존 CSS-001/002/003, IMP-001, TOKEN-001 및 STYLE-001 FAIL은 다른 PASS로 덮지 않음 |
| B — RUNTIME | 실제 custom property/computed 값, 우선순위가 반영된 결과, 지정 상태·폭 | 제출된 이름 있는 측정만 확인. 미실행 Theme·hover/focus/disabled·외부 입력 전체는 NOT VERIFIED |
| C — DESIGN_INTENT | 요청 의미, override 승인, 불명확한 일반 콘텐츠 스타일 | 자동 승인 경로 없음. recipe/hash/행 검토도 NOT VERIFIED 유지 |
STYLE-001은 명시된 HM slot, 실제 공개 HM Component에 연결된 class 또는 공개 HM JSX Component를 대상으로 한 HEX/RGB/HSL 및 0이 아닌 px/rem/em padding/margin/gap/radius 하드코드 override를 FAIL로 판정한다. React 숫자 spacing/radius와 유한 조건부 값, rounded-[13px] 등 임의 utility도 포함한다. var() fallback 속 색상은 직접 하드코드 override와 구분하며, 없는 Token은 TOKEN-001로 계속 FAIL이다. !important, 일반 콘텐츠의 literal 색상·geometry와 Token 기반 Component override는 STYLE-002 검토 위치에 디자인 의도 NOT VERIFIED를 기록한다. 일반 콘텐츠 CSS를 Component 복제로 단정하거나 일괄 FAIL로 바꾸지 않는다. named color, %, calc(), 일반 Tailwind numeric utility 등 모든 CSS 문법을 금지 여부까지 자동 판별하는 것은 아니다. selector의 영향은 GLOBAL/COMPONENT_SCOPED inventory로 제공하며 selector cascade나 격리 보장은 Runtime 대상이다.
tokenEvidence.references는 file/line/name/fallback/origin/status를 기록한다. origin은 PUBLIC/CONSUMER/CONSUMER_INLINE/UNRESOLVED다. 연결된 소비 custom property는 공개 Theme Token과 구분한다. 소비 선언 존재 PASS는 selector별 실제 적용을 뜻하지 않는다. HM 이외 변수도 수집하며 연결 선언이 없는 외부 host 변수는 NOT VERIFIED다. TOKEN-003은 var(--hm-name broken)처럼 명확히 잘못된 참조를 FAIL로 판정한다. 전체 CSS 문법·variable cycle 검사는 별도 한계다.
동적 분석은 문자열/숫자 literal, 모듈 최상위 const, 괄호·TS assertion, 문자열 +, 조건부 분기의 모든 후보와 template 조합을 최대 32개까지 해석한다. shadowed/destructured/function-local binding은 접지 않고 코드를 실행하지 않는다. 후보 중 하나라도 미정의 HM Token이면 FAIL이며 후보 문자열 일부를 가짜 Token 이름으로 검사하지 않는다. 미해석 className/style props/style spread/style value와 동적 Token template은 TOKEN-002 및 tokenEvidence.dynamic 위치 ID로 남긴다. import된 상수·객체 props·외부 입력·상태 전체를 추측하지 않는다. 선언 목록은 동적 위치가 있는 entry에만 출력해 전체 Storybook JSON 크기 증가를 제한한다.
위치별 Runtime 증거는 기존 CSS 측정의 name/kind/selector/property/actual/expected/width와 함께 다음 필드를 요구한다. 실제 source site의 id, file, ruleId를 그대로 복사하고 browser/state/height/component를 실제 검사 환경에서 기록한다. 아래 값은 형식 예시다.
{
"name": "기본 Badge text", "kind": "COMPUTED_STYLE",
"siteId": "Guard가 출력한 실제 위치 ID", "file": "src/App.tsx", "ruleId": "TOKEN-002",
"component": "Badge", "selector": "[data-slot=\"badge\"]", "property": "color",
"width": 390, "height": 900, "state": "default-theme:active",
"browser": { "name": "Chrome", "version": "실제 버전" },
"actualExpression": "var(--hm-sem-fg-primary)", "tokens": ["--hm-sem-fg-primary"],
"actual": "실제 computed 값", "expected": "공개 계약에서 정한 computed 기대값"
}
동적 Token은 actualExpression에서 HM 참조를 추출해 해당 entry의 연결 선언과 tokens를 대조한다. fallback 덕분에 computed 값이 같아도 실제 참조가 미정의면 Runtime FAIL이다. 불완전 graph, 누락된 expression/Token/브라우저 정보, 다른 site/property의 측정은 NOT VERIFIED다. 네 폭에서 명명한 상태의 PASS가 있어야 그 위치의 RUNTIME PASS이며 추가 측정의 FAIL도 보존한다. 기존 형식의 64건 CSS 측정은 측정 PASS로 유지할 수 있지만 새 위치별 증거를 대신하지 않는다. 사용자 제출 증거의 실제 브라우저 실행을 이 도구가 독립 인증하는 것은 아니다.
CSS 선언 위치는 측정 selector가 원본 selector와 같아야 한다. 더 좁은 실제 DOM selector를 사용할 때는 sourceSelector에 원본 selector를 기록하고, 측정한 DOM의 element.matches(sourceSelector) 결과를 matchesSourceSelector: true로 함께 제출한다. Component 이름도 원본 대상과 일치해야 한다. 다른 DOM에서 같은 값이 나온 결과를 가져와 위치 검증으로 사용하지 않는다.
styleVerification.checks는 STATIC/RUNTIME/DESIGN_INTENT/FINAL에 해당하는 staticStatus/runtimeStatus/designIntentStatus/finalStatus, 관련 file/component/rule/DOM/token와 measurement/reason을 반환한다. 필수 결과 중 FAIL이 하나라도 있으면 FINAL FAIL, 미검증이 있으면 NOT VERIFIED다. 동적 위치의 RUNTIME PASS는 관측한 문자열과 스타일만 증명하므로 미확정 분기 전체의 STATIC/FINAL을 PASS로 올리지 않는다. T01의 정적 CSS 연결·Token 선언은 PASS, 세 Badge override의 실제 적용은 별도로 검증할 수 있지만 사용자 의도는 계속 NOT VERIFIED다. 원본 staticStatus의 REUSE-003 기록은 보존한다.
문서 근거는 contractSource 경계·실제 SHA·원본 issue·유효 행 범위를 확인한다. 측정 기대값은 actual 복사가 아니라 공개 recipe에서 정하고, var()가 치환된 브라우저 직렬화 형식으로 비교한다. 일반 CSS 문자열과 computed custom property의 직렬화 차이를 스타일 오류로 보고하지 않도록 측정 도구에서 기대 표현식을 해석한다. 실제 값 mismatch를 느슨한 비교로 숨기지 않는다.
node scripts/ai-generation-guard.mjs --target <기존 앱> --scope FULL_SCREEN --mode DASHBOARD_ONLY --result index.html --json
node scripts/ai-generation-guard.mjs --target <기존 앱> --scope PARTIAL --mode DASHBOARD_ONLY --files src/index.css --result index.html --json
node scripts/guard-evidence.mjs <새 guard.json> <동일 source/contract의 새 runtime.json>
node --test scripts/ai-generation-guard.test.mjs scripts/guard-evidence.test.mjs scripts/guard-requirements.test.mjs
전체 292 Story 재렌더나 신규 AI 화면/모델 호출은 필요 없다. 기존 63/11개 본문은 보존하며, 이 확장 당시 회귀는 Guard 75개·증거 연결 20개·요구사항 연결 5개였다. 최신 자동 회귀 수량은 위 Theme Token 검사 절을 따른다. 과거 원본 FAIL/NOT VERIFIED 증거는 보존하고 새로운 결과를 별도 위치에 저장한다. 문서만 수정하고 검사 소스·계약·화면이 불변이면 유효한 기존 PASS 로그를 재사용하며 정상 suite를 반복하지 않는다.
2026-10-10 필수 스타일 누락 (R1)
완전히 해석한 bootstrap graph에서 필수 Theme(CSS-001), Tailwind utility(CSS-002), 필요한 Dashboard Status(CSS-003)가 없으면 FAIL이다. 해석 불가 import·CSS·bootstrap이 있으면 NOT VERIFIED를 유지한다. exit 0만으로 PASS를 기록하지 않는다. 기존 36개 테스트와 신규 누락 사례 4개를 유지한다.
잔여 판정 회귀 범위 (2026-10-10 안정화)
해당 안정화 당시 기존 40개 테스트 본문을 보존하고 6개를 추가해 46/46을 확인했다. 당시 Guard와 Navigation 분석기 정책·구현은 변경하지 않았다. 이후 확장된 현재 수량은 위 최신 검사 절을 따른다. 당시 실행 범위와 잔여 위험을 함께 확인한다.
| 추가 테스트 | 확인하는 계약 |
|---|---|
| 41 | Storybook PARTIAL에서도 선택 Story의 preview 스타일 연결 누락은 FAIL이다. |
| 42 | computed discovery 대신 명시한 --entry만 검사한다. 선택하지 않은 Story까지 검증했다고 보고하지 않는다. |
| 43 | 현재 import가 해석돼도 상속 tsconfig 전체 해석은 ENTRY-003 NOT VERIFIED다. |
| 44 | 내부 Reference wrapper의 NOT VERIFIED가 같은 Story의 공개 slot 위조 FAIL을 낮추지 않는다. |
| 45 | compact Navigation 증명과 소비 CSS intent 승인은 별개다. 후자는 REUSE-003 NOT VERIFIED다. |
| 46 | Production·Storybook 모두 독립 package 구현을 선택하면 소비 graph 밖 SCOPE-001 NOT VERIFIED다. |
이 테스트 통과는 미검증 판정의 회귀 보호이며 미검증 항목을 PASS로 전환하는 근거가 아니다. 실제 build·브라우저 결과도 원본 Guard JSON 상태를 덮어쓰지 않는다. Reference wrapper의 승인 의도, native control의 합법성, CSS intent, 상속 alias와 opaque 흐름, package 구현의 소비 검사 경계는 계속 구분하여 보고한다.
STYLE-001 Component 식별과 진입점 범위 (2026-10-11)
class의 hm- 접두사만으로 공개 Component를 확정하지 않는다. 기존 known HM slot 이름 범위의 class는 현재 import graph의 JSX/createElement에서 공개 HM Component에 실제 연결되었을 때 계속 검사하며, native 콘텐츠 class는 REUSE-003 및 STYLE-002의 의도 검토와 구분한다. 불명확한 동적 class는 기존 보수적 탐지를 유지한다.
단순 양의 선행 ancestor class와 명시된 descendant HM slot으로 구성된 selector는 현재 진입점의 JSX 및 HTML class 목록을 대조한다. 그 ancestor가 없고 graph/class 입력이 완전하며 공개 package 구현 텍스트에도 같은 이름이 없는 경우 styleEvidence.checks[].applicability.status = NOT APPLICABLE과 근거를 기록한다. 선언은 inventory에서 지우지 않고 RUNTIME/DESIGN_INTENT/FINAL의 NOT VERIFIED를 보존한다. 이는 CSS 승인이나 다른 진입점의 면제가 아니다. 일반 목록 composition class를 새 Component override 대상으로 확장하지 않으며 기존 REUSE-003/STYLE-002 의도 검토를 보존한다.
동적 class, spread, DOM class/HTML 변경(계산된 property 포함), 미해결 import/inline script, selector의 comma·pseudo·escape·nesting은 위 제외 근거로 사용하지 않는다. class가 현재 진입점 또는 HTML에 존재하면 기존 STYLE-001 탐지를 유지한다. Storybook은 기존 Reference 정책을 유지하고 새 일반 업무 화면의 우회 profile로 사용하지 않는다.
이 변경은 일반 콘텐츠 이름 오탐과 현재 사용되지 않는 ancestor 범위의 확정 FAIL만 교정한다. 실제 연결된 HM 하드코드 override·미정의 Token·복제 slot·Navigation 위반 정책은 유지한다. 과거 75/20/5 검사 결과는 당시 기록이다. Component 식별·범위 경계 회귀 11개(76~86)를 추가했으며 새로운 Guard 계약 해시는 이전 Runtime 연결 결과와 구분한다.