fix(color): eliminate redundant dark: accent modifiers (R-8.29), resolve A-1/A-2/A-9 contrast issues, and implement C11-9 quality gate

This commit is contained in:
Godopu committed 2026-08-20 22:42:26 +09:00
1 parent 7e431de1b7
commit a838af4b94
14 files changed
+190 -69

No files matched your search

+126 -12
View File
@@ -4,10 +4,10 @@
| 항목 | 내용 | | 항목 | 내용 |
| :--- | :--- | | :--- | :--- |
| **문서 ID** | `REQ-ANL-WEB-001` | | **문서 ID** | `REQ-ANL-WEB-001` |
| **버전** | `1.12` | | **버전** | `1.14` |
| **작성일** | 2026-07-30 | | **작성일** | 2026-07-30 |
| **작성자** | claude (`canary-projects-landing-page-creator-claude`) · Role: planner | | **작성자** | claude (`canary-projects-landing-page-creator-claude`) · Role: planner |
| **상태** | 승인 (Approved) — v1.5에서 **D-01** 확정(A·B안 기각 / `intl-conf` 폐기 · 3개 카테고리), v1.6에서 **D-06** 확정(레이아웃 컨테이너 3계층 분리). v1.7에서 **§6.1.2 Research Project PageView** 신설 — 신규 미결 **D-07**(🔴 Funder 실데이터 부재)·**D-08**(🟡 프로젝트 집합 범위)은 각 기본값으로 구현을 차단하지 않는다. 잔여 미결 D-02·D-03·D-05는 🟡 등급으로 각 기본값이 적용되어 승인을 보류하지 않는다. v1.9에서 **§8.7 색 토큰 알파 계약**(R-8.21~R-8.24)을 신설하고 **D-09**(Publications 인덱스 라우트)를 확정했다. v1.10에서 개발 팀장(Agy) 이의제기를 반영해 **D-09를 Overview 뷰로 재확정**하고 R-8.25·AC-41을 신설했다. v1.11에서 강조색 교체 커밋(`eaddc4f`)의 대비 붕괴를 규명해 **§8.7에 R-8.26~R-8.28**(강조색 대비 바닥값 · 색값 변경 재측정 의무 · 명암 반전 계약)을 신설하고 **D-10을 확정**(`--cobalt` #4A7CE8 유지 + `--cobalt-dark` #32549E 재조정), **AC-43~AC-46**(대비 게이트 Gate 10 및 그 폴트 인젝션)을 신설했다. 신규 미결 **D-11**(🟡 `text-vermillion` 소형 텍스트 30여 곳)은 현행 유지를 기본값으로 하여 구현을 차단하지 않는다. v1.12에서 개발 팀장(Agy) 이의제기(Job `7f136931`)를 반영해 **D-10의 배지·칩 전경/배경을 재확정**(`bg-cobalt text-ink` 폐기 → `bg-{accent}-dark text-ivory`)하고 **R-8.29**(반전 토큰 쌍의 `dark:` 불필요 원칙)·**AC-47**(팔레트 외 색 상한)을 신설했다 | | **상태** | 승인 (Approved) — v1.5에서 **D-01** 확정(A·B안 기각 / `intl-conf` 폐기 · 3개 카테고리), v1.6에서 **D-06** 확정(레이아웃 컨테이너 3계층 분리). v1.7에서 **§6.1.2 Research Project PageView** 신설 — 신규 미결 **D-07**(🔴 Funder 실데이터 부재)·**D-08**(🟡 프로젝트 집합 범위)은 각 기본값으로 구현을 차단하지 않는다. 잔여 미결 D-02·D-03·D-05는 🟡 등급으로 각 기본값이 적용되어 승인을 보류하지 않는다. v1.9에서 **§8.7 색 토큰 알파 계약**(R-8.21~R-8.24)을 신설하고 **D-09**(Publications 인덱스 라우트)를 확정했다. v1.10에서 개발 팀장(Agy) 이의제기를 반영해 **D-09를 Overview 뷰로 재확정**하고 R-8.25·AC-41을 신설했다. v1.11에서 강조색 교체 커밋(`eaddc4f`)의 대비 붕괴를 규명해 **§8.7에 R-8.26~R-8.28**(강조색 대비 바닥값 · 색값 변경 재측정 의무 · 명암 반전 계약)을 신설하고 **D-10을 확정**(`--cobalt` #4A7CE8 유지 + `--cobalt-dark` #32549E 재조정), **AC-43~AC-46**(대비 게이트 Gate 10 및 그 폴트 인젝션)을 신설했다. 신규 미결 **D-11**(🟡 `text-vermillion` 소형 텍스트 30여 곳)은 현행 유지를 기본값으로 하여 구현을 차단하지 않는다. v1.12에서 개발 팀장(Agy) 이의제기(Job `7f136931`)를 반영해 **D-10의 배지·칩 전경/배경을 재확정**(`bg-cobalt text-ink` 폐기 → `bg-{accent}-dark text-ivory`)하고 **R-8.29**(반전 토큰 쌍의 `dark:` 불필요 원칙)·**AC-47**(팔레트 외 색 상한)을 신설했다. v1.13에서 Primary #4E77FF 전면 적용 커밋군(`ca4a7ee`~`082d71e`)을 검증해 **Gate 0~10 전량 통과 상태에서 AA 미달 6건·통일성 결함 9건**을 실측 확정하고, 게이트 범위 결함 3종에 대해 **R-8.30~R-8.32**(비텍스트 UI 대비 · 역할 단위 색 통일 · 게이트 면제 형식 제한)와 **AC-48~AC-50**(Gate 11 · 폴트 인젝션 F1~F9 · `docs/DESIGN.md` 동기화)을 신설했다. **§8.1을 실제 토큰값으로 동기화**하고 **D-10 확정값을 #345AD2로 개정**했다. 신규 미결 **D-12**(🔴 Primary 적용의 판정 기준 및 히어로 `Orchestration` 시맨틱 규약)는 **A안+B안 병행**을 기본값으로 하여 구현을 차단하지 않으며, **D-13**(🟡 `--vermillion` 반전 1.25)은 현행 유지 + 화이트리스트 등재를 기본값으로 한다. v1.14에서 개발 팀장(Agy) 이의제기(Job `4962a667`)를 반영해 **R-8.33**(토큰 등급 불변식)을 신설하고 **R-8.29를 강화**(강조색 `dark:` 변형 금지 · 현행 위반 38건)했으며, 다크 테마 `bg-paper` 소형 텍스트 **8곳의 AA 미달(4.37)** 과 **G-7**(AC-44 정규식이 다크 절반을 설계상 면제)을 확정해 **AC-51**을 신설했다. 신규 미결 **D-14**(🔴 `--cobalt` 반전 1.00)는 **A안(브랜드 고정 예외)** 을 기본값으로 하되 규격 완화를 포함하므로 **cline·GM 심사 전에는 집행하지 않는다** |
| **대상 독자** | General Manager, Developer Team Leader(Agy), Reviewer Team Leader(Cline), 지도교수 | | **대상 독자** | General Manager, Developer Team Leader(Agy), Reviewer Team Leader(Cline), 지도교수 |
| **선행 문서** | [`README.md`](./README.md), [`.agents/MULTI_AGENT_RULES.md`](./.agents/MULTI_AGENT_RULES.md), [`refer_landing_page/docs/DESIGN.md`](./refer_landing_page/docs/DESIGN.md) | | **선행 문서** | [`README.md`](./README.md), [`.agents/MULTI_AGENT_RULES.md`](./.agents/MULTI_AGENT_RULES.md), [`refer_landing_page/docs/DESIGN.md`](./refer_landing_page/docs/DESIGN.md) |
| **적용 범위** | `refer_landing_page/` (Next.js 14 App Router 애플리케이션) 전체 | | **적용 범위** | `refer_landing_page/` (Next.js 14 App Router 애플리케이션) 전체 |
@@ -414,9 +414,20 @@ interface ResearchProject {
| `ink` / `ink-soft` / `ink-mute` | `#0A0A0A` / `#262420` / `#6B655B` | 본문·괘선 | | `ink` / `ink-soft` / `ink-mute` | `#0A0A0A` / `#262420` / `#6B655B` | 본문·괘선 |
| `ivory` / `paper` / `line` | `#F5F1E8` / `#ECE6D6` / `#D8D1C0` | 종이 배경 / 인셋 / 헤어라인 | | `ivory` / `paper` / `line` | `#F5F1E8` / `#ECE6D6` / `#D8D1C0` | 종이 배경 / 인셋 / 헤어라인 |
| `vermillion` / `vermillion-dark` | `#D9342B` / `#A8231C` | 강조 (Hot) — **최초 커밋 이래 불변** | | `vermillion` / `vermillion-dark` | `#D9342B` / `#A8231C` | 강조 (Hot) — **최초 커밋 이래 불변** |
| `cobalt` / `cobalt-dark` | `#4A7CE8` / `#32549E` *(v1.11 갱신)* | 강조 (Cool) | | `cobalt` / `cobalt-dark` | `#4E77FF` / `#345AD2` *(v1.13 갱신)* | 강조 (Cool) — Primary |
**실측 대비 (v1.13 기준선 · WCAG 2.1 재계산)**
| 토큰 | `ivory`(L) | `paper`(L) | `ivory`(D) | `paper`(D) | 라이트↔다크 반전 대비 |
| :--- | ---: | ---: | ---: | ---: | ---: |
| `cobalt` #4E77FF | 3.45 | 3.12 | 4.82 | 4.37 | **1.00 ❌ (R-8.28 위반)** |
| `cobalt-dark` #345AD2 | 5.26 | **4.76** | 6.57 | 5.96 | 2.08 ✅ |
| `vermillion` #D9342B | 4.16 | 3.77 | 5.01 | 4.54 | **1.25 ❌ (→ D-13)** |
| `vermillion-dark` #A8231C | 6.38 | 5.77 | 6.72 | 6.09 | 2.58 ✅ |
> **v1.11 갱신 근거**: 커밋 `eaddc4f`가 `cobalt`를 `#1B3A8A` → `#4A7CE8`, `cobalt-dark`를 `#122863` → `#3462CC`로 변경했다. `#4A7CE8`은 사용자 지정 값이므로 유지하되, `cobalt-dark`는 `paper` 배경에서 4.48:1로 미달하므로 **D-10 결정에 따라 `#32549E`(paper 5.82:1)로 재조정**한다. `vermillion`(#D9342B)은 변경된 적이 없다 — `eaddc4f`의 커밋 메시지 `…and secondary to #D9342B`는 현상 유지의 서술이다(→ §12.1 D-10 상세). **v1.12**: 두 강조색의 `-dark` 변형(`#A8231C` / `#32549E`)은 소형 텍스트 전경뿐 아니라 **`text-[0.65rem]` 상태 칩의 배경**으로도 사용된다 — `ivory` 전경만으로 6.38~6.43(라이트) / 6.57~6.72(다크)를 확보한다(→ R-8.26 · R-8.29 · §12.1 D-10 보론). > **v1.11 갱신 근거**: 커밋 `eaddc4f`가 `cobalt`를 `#1B3A8A` → `#4A7CE8`, `cobalt-dark`를 `#122863` → `#3462CC`로 변경했다. `#4A7CE8`은 사용자 지정 값이므로 유지하되, `cobalt-dark`는 `paper` 배경에서 4.48:1로 미달하므로 **D-10 결정에 따라 `#32549E`(paper 5.82:1)로 재조정**한다. `vermillion`(#D9342B)은 변경된 적이 없다 — `eaddc4f`의 커밋 메시지 `…and secondary to #D9342B`는 현상 유지의 서술이다(→ §12.1 D-10 상세). **v1.12**: 두 강조색의 `-dark` 변형(`#A8231C` / `#32549E`)은 소형 텍스트 전경뿐 아니라 **`text-[0.65rem]` 상태 칩의 배경**으로도 사용된다 — `ivory` 전경만으로 6.38~6.43(라이트) / 6.57~6.72(다크)를 확보한다(→ R-8.26 · R-8.29 · §12.1 D-10 보론).
>
> **v1.13 갱신 근거**: 커밋 `ca4a7ee`·`8db16e4`가 `cobalt`를 `#4A7CE8` → **`#4E77FF`**, `cobalt-dark`를 D-10 확정값 `#32549E`가 아닌 **`#345AD2`** 로 설정했다. `#4E77FF`는 사용자 지정 Primary이므로 유지하며, `#345AD2`도 `paper` **4.76**으로 R-8.26 바닥값을 충족하므로 **D-10 확정값을 `#345AD2`로 개정**한다(단 여유가 0.26뿐이라 §12.2 R-4로 등재). 다만 **`--cobalt`의 반전 대비가 1.00으로 R-8.28을 다시 위반**하며 — 이는 v1.11에서 D-10의 근본 원인으로 규명한 실패 유형의 재발이다 — AC-45 게이트가 `-dark` 2개 토큰만 검사하도록 구현되어 **검출하지 못했다**(→ R-8.28 개정 · AC-45 개정 · §12.1 D-12 상세). 4개 색 변경 커밋 **전부 본문이 비어 있어 R-8.1·R-8.27의 대비 재산정표 첨부 의무가 4회 연속 미이행**이다.
**R-8.1** 신규 색 토큰 추가 금지. 색상 값 변경 시 `tailwind.config.ts`와 `app/globals.css`의 `:root` CSS 변수를 **반드시 함께** 수정하며, **R-8.26~R-8.28의 대비·반전 제약을 재산정해 첨부**한다. **R-8.1** 신규 색 토큰 추가 금지. 색상 값 변경 시 `tailwind.config.ts`와 `app/globals.css`의 `:root` CSS 변수를 **반드시 함께** 수정하며, **R-8.26~R-8.28의 대비·반전 제약을 재산정해 첨부**한다.
**R-8.2** `brand` / `accent` 별칭은 레거시 호환 전용이며 신규 코드에서 사용 금지. **R-8.2** `brand` / `accent` 별칭은 레거시 호환 전용이며 신규 코드에서 사용 금지.
@@ -429,6 +440,7 @@ interface ResearchProject {
### 8.3 강조색 시맨틱 계약 ### 8.3 강조색 시맨틱 계약
**R-8.5** `vermillion` = Agent Engineering / Orchestration(홀수 항목), `cobalt` = MCP / A2A Protocol(짝수 항목). 목록 렌더 시 인덱스 기반 교차 배정을 따른다. **R-8.5** `vermillion` = Agent Engineering / Orchestration(홀수 항목), `cobalt` = MCP / A2A Protocol(짝수 항목). 목록 렌더 시 인덱스 기반 교차 배정을 따른다.
- **[v1.13 · 적용 범위 명시]** 본 계약은 `docs/DESIGN.md` §2 "의미 있는 색(Semantic accent pairing)"의 정본 서술과 **동일한 내용**이며, 목록 교차 배정뿐 아니라 **단일 강조 표면(히어로 강조어·섹션 키커 등)에도 적용되는지**는 확정되지 않았다. 커밋 `2441f35`가 히어로의 `Orchestration` 강조어를 vermillion → cobalt로 변경해 **같은 페이지 안에서 규약이 둘로 갈린 상태**이므로 **D-12로 이관**한다. 어느 쪽으로 확정하든 **코드와 `docs/DESIGN.md`가 같은 값을 말해야 한다**(→ AC-23 · AC-50).
### 8.4 컴포넌트 재사용 ### 8.4 컴포넌트 재사용
@@ -510,11 +522,13 @@ Job `343b5808` 검증에서 본문 좌우 여백·최대 폭 미적용(B1)이,
**R-8.26 [필수 · 강조색 대비 바닥값] *(v1.11 신설)*** 강조 토큰(`vermillion`·`cobalt`)의 **DEFAULT 변형은 대형 텍스트(≥24 px, 굵게 ≥18.66 px)·도형·테두리·아이콘 전용**이며 **3:1** 이상을 만족한다. **소형 텍스트에는 `-dark` 변형만 사용**하고, `-dark`는 `ivory`·`paper` **양 배경에서 4.5:1 이상**이어야 한다. **R-8.26 [필수 · 강조색 대비 바닥값] *(v1.11 신설)*** 강조 토큰(`vermillion`·`cobalt`)의 **DEFAULT 변형은 대형 텍스트(≥24 px, 굵게 ≥18.66 px)·도형·테두리·아이콘 전용**이며 **3:1** 이상을 만족한다. **소형 텍스트에는 `-dark` 변형만 사용**하고, `-dark`는 `ivory`·`paper` **양 배경에서 4.5:1 이상**이어야 한다.
- `paper`(#ECE6D6)가 `ivory`(#F5F1E8)보다 어두워 **항상 더 엄격한 제약**이므로 바닥값 산정은 `paper` 기준으로 한다. 실측 차이는 약 0.3~0.5 이며, `ivory`만 보고 통과 판정하면 `bg-paper` 카드 내부에서 미달한다. - `paper`(#ECE6D6)가 `ivory`(#F5F1E8)보다 어두워 **항상 더 엄격한 제약**이므로 바닥값 산정은 `paper` 기준으로 한다. 실측 차이는 약 0.3~0.5 이며, `ivory`만 보고 통과 판정하면 `bg-paper` 카드 내부에서 미달한다.
- **[v1.14 개정 · 판정 격자]** 바닥값 판정은 **{라이트, 다크} × {`ivory`, `paper`} 4개 셀 전부**에서 수행한다. **`paper` 기준 산정 원칙은 다크 테마에도 동일하게 적용된다** — 다크 지면 `paper`(`30 28 26`)도 `ivory`(`18 18 18`)보다 밝아 더 엄격하다. v1.12가 지시한 소형 텍스트 라우팅 `text-cobalt-dark dark:text-cobalt`는 **라이트 절반만 `-dark`로 보내고 다크 절반은 DEFAULT로 되돌려**, `bg-paper` 카드 내부 **8곳이 다크 테마에서 4.37로 미달**한다(→ AC-51). 이는 본 규칙을 **한쪽 테마에만 적용한 계획 오류**이며, v1.11 §6.5의 일관성 결함과 같은 형태다.
- 실측 근거: `--cobalt: #4A7CE8`은 `ivory` 3.49 / `paper` 3.16으로 **대형 텍스트 기준(3:1)은 충족하나 소형 텍스트 기준은 미달**한다. 반면 `--cobalt-dark: #32549E`는 6.43 / 5.82로 양 기준을 충족하며, `--vermillion-dark: #A8231C`(6.38 / 5.77)와 **동일 등급**이 되어 두 강조색의 `-dark` 슬롯이 대칭을 이룬다. - 실측 근거: `--cobalt: #4A7CE8`은 `ivory` 3.49 / `paper` 3.16으로 **대형 텍스트 기준(3:1)은 충족하나 소형 텍스트 기준은 미달**한다. 반면 `--cobalt-dark: #32549E`는 6.43 / 5.82로 양 기준을 충족하며, `--vermillion-dark: #A8231C`(6.38 / 5.77)와 **동일 등급**이 되어 두 강조색의 `-dark` 슬롯이 대칭을 이룬다.
- **[v1.12 추가 · 배지·칩 라우팅]** 강조색을 **배경**으로 쓰는 **비인터랙티브 상태 칩**(`text-[0.65rem]` 등급)은 **`bg-{accent}-dark` + `text-ivory`** 를 사용한다. DEFAULT 배경 위에서는 팔레트 내 전경 중 통과 후보가 **0~1개뿐**이라 설계 여유가 없다 — 실측상 `bg-cobalt`(#4A7CE8) 위 4개 후보(`ivory` 3.49 · `white` 3.93 · `ink-soft` 3.94 · `ink` 5.03) 중 통과는 `ink` **1개뿐**이고, `bg-vermillion`(#D9342B) 위 `ivory`는 **4.16으로 미달**한다. `-dark` 배경은 `ivory` 전경만으로 **6.38~6.43(라이트) / 6.57~6.72(다크)** 를 확보한다. - **[v1.12 추가 · 배지·칩 라우팅]** 강조색을 **배경**으로 쓰는 **비인터랙티브 상태 칩**(`text-[0.65rem]` 등급)은 **`bg-{accent}-dark` + `text-ivory`** 를 사용한다. DEFAULT 배경 위에서는 팔레트 내 전경 중 통과 후보가 **0~1개뿐**이라 설계 여유가 없다 — 실측상 `bg-cobalt`(#4A7CE8) 위 4개 후보(`ivory` 3.49 · `white` 3.93 · `ink-soft` 3.94 · `ink` 5.03) 중 통과는 `ink` **1개뿐**이고, `bg-vermillion`(#D9342B) 위 `ivory`는 **4.16으로 미달**한다. `-dark` 배경은 `ivory` 전경만으로 **6.38~6.43(라이트) / 6.57~6.72(다크)** 를 확보한다.
- **[v1.12 추가 · 짝 칩 등급 일치]** 같은 컴포넌트에서 **짝을 이루는 강조색 칩은 동일 변형 등급**을 사용한다. 한쪽만 `-dark`로 내리면 두 칩의 상대휘도 대비가 **1.19 → 1.54로 악화**된다(실측: `vermillion` L=0.1738 ↔ `cobalt-dark` L=0.0949). 양쪽을 `-dark`로 통일하면 **1.01**로 균형이 잡힌다(`vermillion-dark` L=0.0961 ↔ `cobalt-dark` L=0.0949) — 두 강조색의 `-dark` 변형이 접근성 등급뿐 아니라 **휘도까지 대칭**이기 때문이다. - **[v1.12 추가 · 짝 칩 등급 일치]** 같은 컴포넌트에서 **짝을 이루는 강조색 칩은 동일 변형 등급**을 사용한다. 한쪽만 `-dark`로 내리면 두 칩의 상대휘도 대비가 **1.19 → 1.54로 악화**된다(실측: `vermillion` L=0.1738 ↔ `cobalt-dark` L=0.0949). 양쪽을 `-dark`로 통일하면 **1.01**로 균형이 잡힌다(`vermillion-dark` L=0.0961 ↔ `cobalt-dark` L=0.0949) — 두 강조색의 `-dark` 변형이 접근성 등급뿐 아니라 **휘도까지 대칭**이기 때문이다.
- **인터랙티브 내비·토글 요소는 예외**로 DEFAULT 배경 + `text-white dark:text-ivory`(4.69 / 5.01)를 유지한다. 근거는 §12.1 D-10 상세(부류 A/B 구분) 참조. - **인터랙티브 내비·토글 요소는 예외**로 DEFAULT 배경 + `text-white dark:text-ivory`(4.69 / 5.01)를 유지한다. 근거는 §12.1 D-10 상세(부류 A/B 구분) 참조.
- 자동 검증: **AC-42 · AC-43 · AC-44**. - **[v1.13 개정 · `text-white`의 적용 조건]** 위 예외는 **배경이 DEFAULT 변형일 때에 한정**한다. **`-dark` 배경 위 전경은 `ivory` 단독을 정본**으로 하며 `text-white`·`dark:` 변형을 붙이지 않는다 — 실측 `bg-cobalt-dark` 위 `ivory`는 라이트 **5.26** / 다크 **6.57**로 양 테마를 통과하므로 팔레트 외 색이 불필요하다(→ R-8.29). 커밋 `2441f35`가 `Header.tsx:69,129`·`CategoryNav.tsx:22`의 배경을 `bg-vermillion` → `bg-cobalt-dark`로 바꾸면서 **v1.12가 인정했던 `text-white`의 불가피성이 소멸**했다.
- 자동 검증: **AC-42 · AC-43 · AC-44 · AC-47**.
**R-8.27 [필수 · 색값 변경 시 재측정 의무] *(v1.11 신설)*** `--*-rgb` 값을 변경하는 모든 커밋은 **해당 토큰이 참여하는 전 전경/배경 조합의 대비를 재산정**하고 그 표를 커밋 메시지 또는 리뷰 리포트에 첨부한다. **R-8.27 [필수 · 색값 변경 시 재측정 의무] *(v1.11 신설)*** `--*-rgb` 값을 변경하는 모든 커밋은 **해당 토큰이 참여하는 전 전경/배경 조합의 대비를 재산정**하고 그 표를 커밋 메시지 또는 리뷰 리포트에 첨부한다.
- `docs/DESIGN.md`의 강조색 교체 안내(커스터마이즈 훅)에 **이 제약을 명문화**한다. 현행 안내는 "강조색 2개와 `-dark` 변형만 교체하면 됩니다"라고 **교체 방법만** 적고 **지켜야 할 대비 제약을 적지 않아**, 안내를 그대로 따른 커밋 `eaddc4f`가 라이트 테마 대비를 9.23 → 3.49로 붕괴시켰다. 문서가 사고의 직접 원인이므로 문서 교정이 재발 방지의 일부다. - `docs/DESIGN.md`의 강조색 교체 안내(커스터마이즈 훅)에 **이 제약을 명문화**한다. 현행 안내는 "강조색 2개와 `-dark` 변형만 교체하면 됩니다"라고 **교체 방법만** 적고 **지켜야 할 대비 제약을 적지 않아**, 안내를 그대로 따른 커밋 `eaddc4f`가 라이트 테마 대비를 9.23 → 3.49로 붕괴시켰다. 문서가 사고의 직접 원인이므로 문서 교정이 재발 방지의 일부다.
@@ -523,12 +537,42 @@ Job `343b5808` 검증에서 본문 좌우 여백·최대 폭 미적용(B1)이,
**R-8.28 [필수 · 명암 반전 계약] *(v1.11 신설)*** `html.dark`에서 반전되는 모든 토큰은 **라이트 값과 다크 값의 상대휘도가 유의미하게 달라야** 한다 — 두 값 사이의 대비가 **2.0 이상**이어야 한다. **R-8.28 [필수 · 명암 반전 계약] *(v1.11 신설)*** `html.dark`에서 반전되는 모든 토큰은 **라이트 값과 다크 값의 상대휘도가 유의미하게 달라야** 한다 — 두 값 사이의 대비가 **2.0 이상**이어야 한다.
- 근거(실측): 변경 전 `cobalt`는 라이트 `L=0.0509`(#1B3A8A) ↔ 다크 `L=0.2199`(#4D77FF)로 대비 **3.13**이었으나, `eaddc4f` 이후 라이트 `L=0.2170`(#4A7CE8) ↔ 다크 `L=0.2199`로 대비 **1.01**이 되었다. 즉 **라이트 테마가 다크 테마용 밝기를 그대로 반입**했고, 이것이 R-8.26 미달의 직접 원인이다. - 근거(실측): 변경 전 `cobalt`는 라이트 `L=0.0509`(#1B3A8A) ↔ 다크 `L=0.2199`(#4D77FF)로 대비 **3.13**이었으나, `eaddc4f` 이후 라이트 `L=0.2170`(#4A7CE8) ↔ 다크 `L=0.2199`로 대비 **1.01**이 되었다. 즉 **라이트 테마가 다크 테마용 밝기를 그대로 반입**했고, 이것이 R-8.26 미달의 직접 원인이다.
- 한 토큰만 반전 계약에서 이탈하면 그 시점에는 반대 테마가 우연히 무사할 수 있으나, **반대 테마 값을 조정하는 순간 양 테마가 동시에 무너진다.** - 한 토큰만 반전 계약에서 이탈하면 그 시점에는 반대 테마가 우연히 무사할 수 있으나, **반대 테마 값을 조정하는 순간 양 테마가 동시에 무너진다.**
- **[v1.13 개정 · 검사 범위]** 검사 대상은 **`:root`와 `html.dark` 양쪽에 정의된 전 토큰**이다(현행 10종). 일부 토큰만 검사하는 구현은 본 규칙을 충족하지 않는다 — 현행 게이트는 `cobalt-dark`·`vermillion-dark` **2종만** 검사해 **`--cobalt`의 반전 대비 1.00을 검출하지 못했다**. `--vermillion`(1.25)도 미달이나 최초 커밋 이래의 상태이므로 **D-13**으로 분리한다.
- 자동 검증: **AC-45**. - 자동 검증: **AC-45**.
**R-8.29 [필수 · 반전 토큰 쌍의 `dark:` 불필요 원칙] *(v1.12 신설)*** 전경과 배경이 **모두 반전 토큰**(`--*-rgb`로 정의되어 `html.dark`에서 값이 바뀌는 토큰)이면 **`dark:` 변형 없이 양 테마가 성립해야 한다.** 어떤 조합에 `dark:` 변형이 필요하다면 그것은 **반전하지 않는 색(`white`·리터럴 hex 등)이 섞였다는 신호**이므로, 팔레트 외 색을 제거할 수 있는지 **먼저 검토**한다. **R-8.29 [필수 · 반전 토큰 쌍의 `dark:` 불필요 원칙] *(v1.12 신설)*** 전경과 배경이 **모두 반전 토큰**(`--*-rgb`로 정의되어 `html.dark`에서 값이 바뀌는 토큰)이면 **`dark:` 변형 없이 양 테마가 성립해야 한다.** 어떤 조합에 `dark:` 변형이 필요하다면 그것은 **반전하지 않는 색(`white`·리터럴 hex 등)이 섞였다는 신호**이므로, 팔레트 외 색을 제거할 수 있는지 **먼저 검토**한다.
- 실측 대조: `bg-cobalt-dark text-ivory`는 라이트 **6.43** / 다크 **6.57**로 `dark:` 없이 통과한다. 반면 `bg-cobalt text-ink`는 다크에서 `--ink-rgb`가 `245 241 232`로 반전되어 **3.49로 미달**하므로 `dark:text-ivory`를 수동으로 덧대야 한다 — **반전 계약을 손으로 우회하는 구조**이며 R-8.28의 취지에 역행한다. - 실측 대조: `bg-cobalt-dark text-ivory`는 라이트 **6.43** / 다크 **6.57**로 `dark:` 없이 통과한다. 반면 `bg-cobalt text-ink`는 다크에서 `--ink-rgb`가 `245 241 232`로 반전되어 **3.49로 미달**하므로 `dark:text-ivory`를 수동으로 덧대야 한다 — **반전 계약을 손으로 우회하는 구조**이며 R-8.28의 취지에 역행한다.
- `white`가 불가피한 경우(`bg-vermillion` 위 `ivory`가 4.16으로 미달하는 인터랙티브 요소)는 **AC-47 화이트리스트**로 상한을 관리한다. 화이트리스트는 상한이지 허가가 아니다 — 신규 사용처는 먼저 `-dark` 배경으로 대체 가능한지 검토한다. - `white`가 불가피한 경우(`bg-vermillion` 위 `ivory`가 4.16으로 미달하는 인터랙티브 요소)는 **AC-47 화이트리스트**로 상한을 관리한다. 화이트리스트는 상한이지 허가가 아니다 — 신규 사용처는 먼저 `-dark` 배경으로 대체 가능한지 검토한다.
- 자동 검증: **AC-47**. - **[v1.14 강화 · 강조색 `dark:` 금지]** **강조색(`cobalt`·`vermillion` 및 각 `-dark`)에는 `dark:` 변형을 사용하지 않는다.** 반전 토큰 위에 `dark:`를 덧대는 것은 **반전 계약을 손으로 무력화**하는 행위이며, 실제로 `text-cobalt-dark dark:text-cobalt`는 다크 절반을 DEFAULT로 되돌려 **`bg-paper` 위 4.37**을 만든다. `-dark` 단독은 4개 셀 전부(5.26 / 4.76 / 6.57 / 5.96)를 통과한다.
- 세 가지 정본 패턴만 사용한다 — ⓐ 대형 텍스트(≥24 px)·도형·테두리·아이콘·SVG: **`text-cobalt` / `bg-cobalt` / `border-cobalt`**(3.12~4.82, 바닥값 3.0), ⓑ 소형 텍스트: **`text-cobalt-dark`**(4.76~6.57, 바닥값 4.5), ⓒ 강조 배경: **`bg-cobalt-dark text-ivory`**(5.26 / 6.57). **세 패턴 모두 `dark:` 변형이 없다.**
- 현행 위반 **38건**(`dark:text-cobalt` 28 · `dark:bg-cobalt` 6 · `dark:hover:border-cobalt` 2 · `dark:hover:text-cobalt` 1 · `dark:group-hover:text-cobalt` 1) 전량이 제거 대상이다.
- 자동 검증: **AC-47 · AC-48(C11-9) · AC-51**.
**R-8.30 [필수 · 비텍스트 UI 대비 계약] *(v1.13 신설)*** 테두리·포커스 링·아이콘·상태 도형 등 **비텍스트 UI 구성요소**(WCAG 2.1 §1.4.11)는 인접색 대비 **3:1** 이상을 만족한다.
- **슬래시 알파 수식자를 사용한 색은 배경과 합성한 실효색으로 산정**한다. 알파는 대비를 조용히 갉아먹으며 토큰 값만으로는 판정할 수 없다 — 실측: `focus:ring-cobalt/50`은 `cobalt` 자체가 3.45임에도 `ivory` 위 합성 후 **1.80**(다크 2.07), `hover:border-cobalt/60`은 `paper` 위 **1.95**(다크 2.38)로 **양쪽 다 미달**한다.
- 이는 §12.1 D-09(알파 계약 붕괴)·§12.1 D-10(반전 계약 이탈)에 이은 **"알파·토큰 조합이 무성으로 대비를 무너뜨리는" 유형의 세 번째 발현**이다. 강조색에 알파 수식자를 붙이는 것은 **원칙적으로 지양**하고, 필요하면 `-dark` 변형을 불투명하게 사용한다.
- 통과 확인된 사용처(회귀 방어 기준선): `hover:border-cobalt` 3.12 / `hover:border-cobalt-dark` 4.76 / `hover:border-vermillion` 3.77 / `ring-vermillion` 4.16 / 불릿 `bg-cobalt` 3.45 / 히어로 SVG `var(--cobalt)` 3.45 · `var(--vermillion)` 4.16 (전부 `paper` 또는 `ivory` 기준 라이트값).
- 자동 검증: **AC-48**(Gate 11 · C11-5).
**R-8.31 [필수 · 역할 단위 색 통일 계약] *(v1.13 신설)*** **동일한 클래스 시그니처를 갖는 요소는 단일 색 패턴을 사용한다.** 역할은 파일·행이 아니라 시그니처로 식별한다.
- 판정 대상 시그니처(최소): ⓐ 섹션 장식 괘선 `h-px w-8 bg-*`, ⓑ 섹션 키커 `font-mono text-xs … uppercase tracking-widest … font-bold`, ⓒ 강조 배경 배지·칩의 전경, ⓓ 카드 `hover:border-*`, ⓔ 포커스 인디케이터 `ring-*`.
- 판정 기준은 **"어떤 색인가"가 아니라 "색 패턴이 하나뿐인가"** 이다. 따라서 색 선택 결정(D-12)이 확정되기 전에도 게이트를 구현·운용할 수 있고, 결정이 바뀌어도 게이트는 수정할 필요가 없다.
- 실측 위반: ⓐ `bg-cobalt-dark dark:bg-cobalt` 6곳 ↔ `bg-vermillion` 1곳(`DirectionsBlock.tsx:7`), ⓑ `text-cobalt-dark dark:text-cobalt` 11곳 ↔ `text-vermillion` 1곳(`DirectionsBlock.tsx:8`), ⓒ `text-white dark:text-ivory` 6곳 ↔ `text-ivory` 4곳, ⓓ 4종, ⓔ 2종.
- **같은 데이터가 서로 다른 색으로 렌더되는 것도 위반**이다 — 지도교수 이메일 `sjkoh@knu.ac.kr` 한 값이 `members:54`(5.26) · `DirectionsBlock:43`(4.16) · `Footer:53`(paper 위 hover 3.77)에서 **3색 3대비**로 나타난다.
- 예외는 **화이트리스트 등재**(R-8.32 형식)로만 허용한다.
- 자동 검증: **AC-48**(Gate 11 · C11-1~C11-4).
**R-8.32 [필수 · 게이트 면제 형식 제한] *(v1.13 신설)*** 자동 게이트의 예외는 **`(파일, 행, 사유)` 3-튜플 등재로만** 허용하며, **라인 부분문자열 면제(`if "…" in line: continue`)를 금지**한다. 등재 항목이 실제 소스에 존재하지 않으면(고아 항목) 게이트는 **실패**해야 한다.
- 근거(실측): `run_e2e_tests.py`의 AC-44 구현은 면제 조항에 `"text-vermillion" in line`을 두었는데, 위반 판정 정규식이 `text-(cobalt|vermillion)`이므로 **vermillion으로 매치된 라인은 예외 없이 그 문자열을 포함**한다 — 즉 **AC-44의 vermillion 절반이 영구적으로 발화하지 않는다.** 실제 소스 4개 라인(`lectures:50`·`members:114`·`publications:72`·`DirectionsBlock:8`)과 회귀 주입 2건을 동일 로직에 투입해 **전부 면제됨**을 재현했다.
- 나아가 `"text-3xl"`·`"Pillar #"` 등의 면제도 **라인 전체 부분문자열 검사**라, 삼항식 한 줄에 두 색이 섞이면 **cobalt 검사까지 함께 무력화**된다 — 회귀가 가장 숨기 쉬운 형태가 정확히 그 형태다.
- 이는 **N6**(Gate 8 C1/C2 공허 단정, 10라운드 미해소)과 **동일 유형**이다. 개별 수정이 아니라 **형식 차원의 금지**로 대응한다.
- 자동 검증: **AC-48 · AC-49**(F2 · F9).
**R-8.33 [필수 · 토큰 등급 불변식] *(v1.14 신설)*** 각 테마에서 **`-dark` 변형은 해당 테마의 지면 배경(`ivory`·`paper`) 대비가 DEFAULT보다 커야** 한다. DEFAULT 토큰의 값을 조정할 때는 **같은 테마의 `-dark` 값과의 관계를 반드시 함께 검토**한다.
- **절대 휘도 순서가 테마 간에 뒤집히는 것은 정상 동작이며 위반이 아니다.** `-dark`는 "절대적으로 어두운 색"이 아니라 **"지면 대비가 더 깊은 변형"** 이므로, 다크 지면에서 대비를 깊게 하려면 더 밝아진다. 실측: 라이트 `cobalt` L=0.2203 > `cobalt-dark` L=0.1270 이지만 다크에서는 `cobalt` L=0.2199 < `cobalt-dark` L=0.3184이며, vermillion 쌍도 동일하게 뒤집힌다. **판정 기준을 절대 휘도로 두면 정상 상태를 위반으로 오판한다.**
- 현행 실측(8개 셀 전부 충족): 라이트 `ivory` 3.45→5.26 · 라이트 `paper` 3.12→4.76 · 다크 `ivory` 4.82→6.57 · 다크 `paper` 4.37→5.96 (cobalt), 라이트 `ivory` 4.16→6.38 · 다크 `ivory` 5.01→6.72 (vermillion).
- 근거: 개발 팀장(Agy) 이의제기(Job `4962a667`)가 **다크 DEFAULT만 단독 상향하면 계층이 무너짐**을 지적했고 실측으로 확인했다 — 다크 DEFAULT를 L≈0.52로 올리면 다크 `paper` 대비가 **9.2**가 되어 `-dark`(5.96)를 역전한다.
- 자동 검증: **AC-43**(정책 표에 등급 관계를 포함).
--- ---
@@ -635,18 +679,51 @@ Job `343b5808` 검증에서 본문 좌우 여백·최대 폭 미적용(B1)이,
- [ ] AC-41 *(v1.10 신설 · R-8.25)* 산출 정적 HTML 전 라우트의 **`<main>` 내부 콘텐츠를 정규화 해시**해 **동일 해시가 2건 이상 존재하지 않음**을 자동 게이트로 확인한다. `canonical`이 명시된 예외 쌍만 화이트리스트로 허용한다 - [ ] AC-41 *(v1.10 신설 · R-8.25)* 산출 정적 HTML 전 라우트의 **`<main>` 내부 콘텐츠를 정규화 해시**해 **동일 해시가 2건 이상 존재하지 않음**을 자동 게이트로 확인한다. `canonical`이 명시된 예외 쌍만 화이트리스트로 허용한다
- [ ] AC-42 *(v1.10 신설 · v1.11 개정 · R-9.6 · AC-38 확장)* **vermillion 배경 위 텍스트 전량**이 라이트·다크 양 테마에서 4.5:1 이상임을 확인한다. **"전량"은 Header·`CategoryNav`에 한정되지 않으며, `group-open:`·`hover:` 등 상태 변형으로 적용되는 배경도 포함한다.** 검증 대상은 **두 부류로 나누어 각각의 기대 패턴과 기대 대비를 판정**한다*(v1.12 개정)*. - [ ] AC-42 *(v1.10 신설 · v1.11 개정 · R-9.6 · AC-38 확장)* **vermillion 배경 위 텍스트 전량**이 라이트·다크 양 테마에서 4.5:1 이상임을 확인한다. **"전량"은 Header·`CategoryNav`에 한정되지 않으며, `group-open:`·`hover:` 등 상태 변형으로 적용되는 배경도 포함한다.** 검증 대상은 **두 부류로 나누어 각각의 기대 패턴과 기대 대비를 판정**한다*(v1.12 개정)*.
- **부류 A — `text-[0.65rem]` 비인터랙티브 상태 칩**: `app/standardization/page.tsx:40`·`:88` → **`bg-vermillion-dark text-ivory`**(라이트 **6.38** / 다크 **6.72**, `dark:` 변형 없음). R-8.26 배지·칩 라우팅 조항 적용 - **부류 A — `text-[0.65rem]` 비인터랙티브 상태 칩**: `app/standardization/page.tsx:40`·`:88` → **`bg-vermillion-dark text-ivory`**(라이트 **6.38** / 다크 **6.72**, `dark:` 변형 없음). R-8.26 배지·칩 라우팅 조항 적용
- **부류 B — 인터랙티브 내비·토글**: `components/Header.tsx:69,129`(교정 완료), `CategoryNav.tsx:22`(교정 완료), `app/lectures/page.tsx:89`·`app/members/page.tsx:177`(미교정) → **`text-white dark:text-ivory`**(라이트 **4.69** / 다크 **5.01**). Header·`CategoryNav` 2곳은 `02247a0`에서 교정되어 재작업 대상이 아니다 - **부류 B — 인터랙티브 내비·토글**: `components/Header.tsx:69,129`, `CategoryNav.tsx:22`, `app/lectures/page.tsx:89`, `app/members/page.tsx:177` → **`text-white dark:text-ivory`**(라이트 **4.69** / 다크 **5.01**)
- **[v1.13 개정 · 부류 B 재산정]** ⓐ `Header.tsx:69,129`·`CategoryNav.tsx:22`는 `2441f35`에서 **배경이 `bg-vermillion` → `bg-cobalt-dark`로 바뀌어** `text-white`의 불가피성이 소멸했다 — R-8.26 v1.13 조항에 따라 **`text-ivory` 단독**(라이트 5.26 / 다크 6.57)으로 정정한다. ⓑ **`app/lectures/page.tsx:89`는 여전히 `group-open:text-ivory`(4.16)로 미교정**이며, 동일 관용구인 `members/page.tsx:177`만 교정되어 **한 쌍이 갈라져 있다**. ⓒ **`app/not-found.tsx:22`를 신규 등재** — `text-ivory hover:bg-vermillion`으로 hover 시 **4.16**이며 v1.12까지의 어떤 목록에도 없었다. 권고 조치는 `hover:bg-vermillion-dark`(→ 6.38)로, `white` 없이 해소된다
- *v1.10 기재 정정*: `text-white` on `#D9342B`의 실측은 **4.69:1**이다(직전 수렴 리포트의 4.58:1은 오기 — 판정은 동일하게 통과) - *v1.10 기재 정정*: `text-white` on `#D9342B`의 실측은 **4.69:1**이다(직전 수렴 리포트의 4.58:1은 오기 — 판정은 동일하게 통과)
- *현행 미교정 4곳의 실측은 모두 **4.16:1***이며, `lectures:89`·`members:177`은 `group-open:` 상태 변형으로 배경이 적용되어 **육안 리뷰에서 누락되기 쉽다** - *현행 미교정 4곳의 실측은 모두 **4.16:1***이며, `lectures:89`·`members:177`은 `group-open:` 상태 변형으로 배경이 적용되어 **육안 리뷰에서 누락되기 쉽다**
- [ ] AC-43 *(v1.11 신설 · R-8.26·R-8.27·R-9.6)* **대비 자동 게이트(Gate 10)** — `app/globals.css`의 `:root`·`html.dark` `--*-rgb` 정의를 파싱해 **양 테마 전 조합의 대비를 계산**하고 정책 표의 바닥값 미만이면 실패한다. 최소 대상: `{ink, ink-soft, ink-mute, vermillion, vermillion-dark, cobalt, cobalt-dark} × {ivory, paper}` 및 `{ink, ivory, white} × {vermillion, vermillion-dark, cobalt, cobalt-dark}`. **`-dark` 변형이 배경으로 쓰이는 경우**(`{ivory, white} × {cobalt-dark, vermillion-dark}`, 양 테마)를 반드시 포함한다 — v1.11 목록은 `-dark`를 전경으로만 다루어 **상태 칩 배경을 검사하지 못했다***(v1.12 개정)*. 정책 표는 스크립트 상단 상수 블록에 두어 규격 변경 시 1곳만 수정되게 한다. *(현행 실측: `cobalt`는 `ivory` 3.49 / `paper` 3.16, `cobalt-dark`는 `paper` 4.48로 **실패해야 한다**)* - [ ] AC-43 *(v1.11 신설 · v1.12·v1.13 개정 · R-8.26·R-8.27·R-9.6)* **대비 자동 게이트(Gate 10)** — `app/globals.css`의 `:root`·`html.dark` `--*-rgb` 정의를 파싱해 **양 테마 전 조합의 대비를 계산**하고 정책 표의 바닥값 미만이면 실패한다. 최소 대상: `{ink, ink-soft, ink-mute, vermillion, vermillion-dark, cobalt, cobalt-dark} × {ivory, paper}` 및 `{ink, ivory, white} × {vermillion, vermillion-dark, cobalt, cobalt-dark}`. **`-dark` 변형이 배경으로 쓰이는 경우**(`{ivory, white} × {cobalt-dark, vermillion-dark}`, 양 테마)를 반드시 포함한다 — v1.11 목록은 `-dark`를 전경으로만 다루어 **상태 칩 배경을 검사하지 못했다***(v1.12 개정)*. 정책 표는 스크립트 상단 상수 블록에 두어 규격 변경 시 1곳만 수정되게 한다. *(현행 실측: `cobalt`는 `ivory` 3.49 / `paper` 3.16, `cobalt-dark`는 `paper` 4.48로 **실패해야 한다**)*
- [ ] AC-44 *(v1.11 신설 · R-8.26)* **소형 텍스트 라우팅 게이트** — `app/**`·`components/**`에서 `text-cobalt`·`text-vermillion`(DEFAULT 변형)이 **소형 텍스트 크기 클래스와 동일 요소에 공존하지 않음**을 확인한다. 부모로부터 크기를 상속받아 정적 판정이 불가능한 케이스는 **화이트리스트에 사유와 함께 등재**한다. *(현행 실측: `text-cobalt` 소형 텍스트 사용처 **12건** + `bg-cobalt` 소형 배지 **2건** 미달)* - **[v1.13 개정 · C10-1]** 현행 구현은 ⓐ 정책 값이 **함수 본문에 흩어져 있어** 상수 블록 요건을 충족하지 않고, ⓑ **토큰 값만 검사하고 소스의 전경/배경 쌍은 보지 않는다** — 따라서 `bg-cobalt-dark text-white`(다크 **2.85**)를 작성해도 통과한다. 검사 대상에 **소스에서 실제로 함께 쓰인 전경/배경 조합**을 추가하고, `text-white`가 전경인 조합을 **양 테마 모두** 계산한다
- [ ] AC-45 *(v1.11 신설 · R-8.28)* **반전 계약 게이트** — `:root`와 `html.dark`의 동일 토큰 쌍에 대해 라이트↔다크 상대휘도 대비가 **2.0 이상**임을 확인한다. *(현행 실측: `cobalt`는 **1.01**로 실패해야 한다)* - **[v1.14 개정]** ⓐ **DEFAULT 토큰 × {`ivory`,`paper`} × {라이트,다크}** 를 **3:1 바닥값**으로 명시 추가한다 — 이 검사가 **D-14 A안의 안전망 본체**이며, 브랜드 색이 어느 테마에서든 역할 바닥값 아래로 내려가면 즉시 실패해야 한다. ⓑ **R-8.33 등급 관계**(각 테마 지면 대비에서 `-dark` > DEFAULT)를 정책 표에 포함한다
- [ ] AC-44 *(v1.11 신설 · v1.13 개정 · R-8.26·R-8.32)* **소형 텍스트 라우팅 게이트** — `app/**`·`components/**`에서 `text-cobalt`·`text-vermillion`(DEFAULT 변형)이 **소형 텍스트 크기 클래스와 동일 요소에 공존하지 않음**을 확인한다. 부모로부터 크기를 상속받아 정적 판정이 불가능한 케이스는 **화이트리스트에 사유와 함께 등재**한다. *(현행 실측: `text-cobalt` 소형 텍스트 사용처 **12건** + `bg-cobalt` 소형 배지 **2건** 미달)*
- **[v1.13 개정 · C10-2]** 현행 구현의 **라인 부분문자열 면제 4종(`"text-vermillion"`·`"text-3xl"`·`"Pillar #"`·`"w-1.5 h-1.5"`)을 전량 제거**하고 R-8.32의 `(파일, 행, 사유)` 화이트리스트로 대체한다. 첫 번째 면제 때문에 **AC-44의 vermillion 절반이 구조적으로 발화하지 않음**을 재현 확인했다(→ R-8.32). 제거 후 `text-vermillion` **23건** 중 소형 텍스트 사용처가 검출되어야 하며, 최저값은 `bg-paper` 카드 내부의 `members/page.tsx:114,144` **3.77**이다
- **[v1.14 개정 · G-7]** 판정 정규식의 **부정 후방탐색 `(?<!dark:)` 를 제거**한다. 현행 구현은 이 후방탐색으로 **`dark:text-cobalt`·`dark:text-vermillion`을 설계상 검사 대상에서 제외**하고 있어, 소형 텍스트 라우팅을 **라이트 테마에 대해서만** 검사한다. 그 결과 다크 테마 `bg-paper` 위 **8곳(4.37)이 검사 범위 밖**이었다(→ AC-51). G-1(부분문자열 면제)·G-2(반전 검사 2종 한정)·G-3(전경/배경 쌍 미검사)에 이은 **네 번째 범위 결함이며, 넷 모두 "검사를 절반만 한다"는 같은 형태**다
- [ ] AC-45 *(v1.11 신설 · v1.13 개정 · R-8.28)* **반전 계약 게이트** — `:root`와 `html.dark`의 동일 토큰 쌍에 대해 라이트↔다크 상대휘도 대비가 **2.0 이상**임을 확인한다. *(현행 실측: `cobalt`는 **1.01**로 실패해야 한다)*
- **[v1.13 개정 · C10-3]** 검사 범위를 **양쪽에 정의된 전 토큰(현행 10종)** 으로 확대한다. 현행 구현은 `cobalt-dark`·`vermillion-dark` **2종만** 검사해 **`--cobalt`의 반전 대비 1.00(라이트 `78 119 255` ↔ 다크 `77 119 255`)을 검출하지 못했다** — v1.11이 D-10의 근본 원인으로 규명한 바로 그 실패 유형의 재발이며, **게이트가 통과했다는 사실 자체가 오탐 신호였다**. 확대 시 `--vermillion`(1.25)도 검출되므로 **D-13 확정 전까지는 화이트리스트에 사유와 함께 등재**한다
- **[v1.14 · 브랜드 고정 토큰 예외 후보]** `--cobalt`(1.00)·`--vermillion`(1.25)은 **사용자 지정 브랜드 색**이며, R-8.29 정리(강조색 `dark:` 변형 38건 제거) 후에는 **어느 테마에서도 소형 텍스트를 담지 않는다**. 남는 사용처는 전부 대형 텍스트·도형·테두리·아이콘이며 **양 테마에서 3:1을 충족**한다(최저 3.12). 이 상태에서 R-8.28을 관철하려면 다크 값을 파스텔로 올려야 하는데, 그러면 **브랜드 색이 다크 테마에서 사라진다**. 처리 방침은 **D-14**로 이관한다 — R-8.28은 *수단*이고 **R-8.26·R-8.30(테마별 대비)이 *목적*** 이므로, 목적 규칙이 **양 테마에서 실효적으로 검사되면**(→ AC-43 개정) 안전망은 유지된다. 검증: 브랜드 색을 `#1B3A8A`로 되돌리고 테마 불변을 유지하면 다크 지면 대비가 **1.80**으로 떨어져 **AC-43이 즉시 실패**한다
- [ ] AC-46 *(v1.11 신설 · AC-43·AC-44·AC-45 유효성)* **폴트 인젝션으로 Gate 10의 실효성을 증명**한다 — ① `--cobalt-dark-rgb`를 `52 98 204`로 되돌리면 AC-43 실패, ② 소형 텍스트를 `text-cobalt`(DEFAULT)로 되돌리면 AC-44 실패, ③ `html.dark`의 `--cobalt-rgb`를 라이트 값과 동일하게 두면 AC-45 실패, ④ `standardization/page.tsx:40`을 `bg-vermillion text-ivory`로 되돌리면 AC-42 실패, **⑤ 상태 칩을 `bg-cobalt text-ivory`(v1.11 §6.5 폐기안)로 되돌리면 AC-43 실패(3.49)**, **⑥ 임의 위치에 `text-white`를 1건 추가하면 AC-47 실패(9 > 8)** *(⑤·⑥은 v1.12 신설)*. **6건 모두 실패를 재현하지 못하면 Gate 10을 채택하지 않는다** — 통과만으로는 게이트가 결함을 검출한다는 증명이 되지 않는다 - [ ] AC-46 *(v1.11 신설 · AC-43·AC-44·AC-45 유효성)* **폴트 인젝션으로 Gate 10의 실효성을 증명**한다 — ① `--cobalt-dark-rgb`를 `52 98 204`로 되돌리면 AC-43 실패, ② 소형 텍스트를 `text-cobalt`(DEFAULT)로 되돌리면 AC-44 실패, ③ `html.dark`의 `--cobalt-rgb`를 라이트 값과 동일하게 두면 AC-45 실패, ④ `standardization/page.tsx:40`을 `bg-vermillion text-ivory`로 되돌리면 AC-42 실패, **⑤ 상태 칩을 `bg-cobalt text-ivory`(v1.11 §6.5 폐기안)로 되돌리면 AC-43 실패(3.49)**, **⑥ 임의 위치에 `text-white`를 1건 추가하면 AC-47 실패(9 > 8)** *(⑤·⑥은 v1.12 신설)*. **6건 모두 실패를 재현하지 못하면 Gate 10을 채택하지 않는다** — 통과만으로는 게이트가 결함을 검출한다는 증명이 되지 않는다
- ⑤는 **기각·교체된 설계를 게이트가 실제로 잡는지** 확인한다. v1.10에서 기각한 D-09 C안을 주입해 AC-41을 검증한 것과 같은 방식이며, **교체된 설계는 반드시 게이트의 검출 대상이 되어야 한다** - ⑤는 **기각·교체된 설계를 게이트가 실제로 잡는지** 확인한다. v1.10에서 기각한 D-09 C안을 주입해 AC-41을 검증한 것과 같은 방식이며, **교체된 설계는 반드시 게이트의 검출 대상이 되어야 한다**
- [ ] AC-47 *(v1.12 신설 · R-8.29)* **팔레트 외 색 상한 게이트** — `app/**`·`components/**`에서 `text-white`·`bg-white` 등 **팔레트 외 색 유틸리티의 출현 횟수가 화이트리스트 등재 건수를 초과하지 않음**을 확인한다. - [ ] AC-47 *(v1.12 신설 · v1.13 개정 · R-8.29)* **팔레트 외 색 상한 게이트** — `app/**`·`components/**`에서 `text-white`·`bg-white` 등 **팔레트 외 색 유틸리티의 출현 횟수가 화이트리스트 등재 건수를 초과하지 않음**을 확인한다.
- 화이트리스트(부류 B 한정, 총 **8건**): `components/Header.tsx:69,75,129,136,145`(5) · `CategoryNav.tsx:22`(1) · `lectures/page.tsx:89`(1) · `members/page.tsx:177`(1) - 화이트리스트(부류 B 한정, 총 **8건**): `components/Header.tsx:69,75,129,136,145`(5) · `CategoryNav.tsx:22`(1) · `lectures/page.tsx:89`(1) · `members/page.tsx:177`(1)
- 각 항목에 **사유**(해당 위치에서 `ivory` 전경이 4.16으로 미달)를 함께 기재한다. 화이트리스트는 **상한이지 허가가 아니며**, 신규 사용처는 `-dark` 배경으로 대체 가능한지 먼저 검토한 뒤에만 등재한다 - 각 항목에 **사유**(해당 위치에서 `ivory` 전경이 4.16으로 미달)를 함께 기재한다. 화이트리스트는 **상한이지 허가가 아니며**, 신규 사용처는 `-dark` 배경으로 대체 가능한지 먼저 검토한 뒤에만 등재한다
- 근거: `docs/DESIGN.md`는 "토큰만 사용 — 임의 회색·팔레트 밖 hex 금지"를 규정한다. `white`는 Issue 01 팔레트(`ink`/`ivory`/`paper`/`line`/`vermillion`/`cobalt`) 밖의 색으로, `02247a0`에서 **불가피성 때문에** 도입되었다. 상한을 두지 않으면 그 불가피성이 관행으로 굳는다 - 근거: `docs/DESIGN.md`는 "토큰만 사용 — 임의 회색·팔레트 밖 hex 금지"를 규정한다. `white`는 Issue 01 팔레트(`ink`/`ivory`/`paper`/`line`/`vermillion`/`cobalt`) 밖의 색으로, `02247a0`에서 **불가피성 때문에** 도입되었다. 상한을 두지 않으면 그 불가피성이 관행으로 굳는다
- **[v1.13 개정 · C10-4]** 현행 구현은 `content.count("text-white")` **단순 계수**라 **위치를 검증하지 않는다.** 화이트리스트를 `(파일, 행)` 집합으로 두고 **실제 출현 위치 집합과 완전히 일치**할 때만 통과시킨다(초과·고아 양방향 검출, R-8.32). 배경 변경(`bg-vermillion` → `bg-cobalt-dark`)으로 **현행 7건 중 6건이 R-8.29상 제거 대상**이 되었으므로, Phase 3 완료 후 화이트리스트는 **`lectures/page.tsx:89`·`members/page.tsx:177` 2건**으로 축소되어야 한다 — 이 2곳만이 DEFAULT `bg-vermillion` 위(ivory 4.16 미달)에 있다
**색 통일성 (v1.13 신설)**
- [ ] AC-48 *(v1.13 신설 · R-8.30·R-8.31·R-8.32)* **색 통일성 게이트(Gate 11)** — 아래 8개 검사를 자동화한다. C11-1~C11-4는 R-8.31, C11-5는 R-8.30, C11-7은 AC-23 회귀 방어에 대응한다.
- **C11-1 괘선 통일** — `h-px w-8 bg-*` 시그니처의 색 클래스가 **2종 이상이면 실패** *(현행: `bg-cobalt-dark dark:bg-cobalt` 6곳 ↔ `bg-vermillion` 1곳 → **실패해야 한다**)*
- **C11-2 키커 통일** — `font-mono text-xs … uppercase tracking-widest … font-bold` 시그니처의 색 클래스가 2종 이상이면 실패 *(현행 11곳 ↔ 1곳 → **실패해야 한다**)*
- **C11-3 강조 배경 전경 통일** — `bg-{accent}-dark` 요소에 `text-white`가 결합되어 있으면 실패(R-8.29) *(현행 6건 → **실패해야 한다**)*
- **C11-4 상태 배경 전경 쌍** — `group-open:bg-*`·`hover:bg-*`로 배경이 바뀌는 요소의 대응 상태 전경과의 대비가 4.5 미만이면 실패 *(현행 `lectures:89` 4.16 · `not-found:22` 4.16 → **실패해야 한다**)*
- **C11-5 비텍스트 UI 3:1** — `border-{accent}*`·`ring-{accent}*`·도형 `bg-{accent}*`·SVG의 `var(--*)` 참조를 수집하고, **슬래시 알파는 배경과 합성한 뒤** 계산해 3.0 미만이면 실패 *(현행 `focus:ring-cobalt/50` **1.80** · `hover:border-cobalt/60` **1.95** → **실패해야 한다**)*
- **C11-6 전역 CSS 조합** — `globals.css`의 `background`/`color` 쌍(`::selection` 등)을 파싱해 4.5 미만이면 실패 *(현행 `::selection`은 `ivory` on `vermillion` **4.16** → **실패해야 한다**)*
- **C11-7 문서 동기화** — `docs/DESIGN.md` §3 색상표의 hex ↔ `globals.css`의 `--*-rgb` 대조, 불일치 시 실패 *(현행 일치 — 회귀 방어용)*
- **C11-8 시맨틱 페어링** — R-8.5의 `vermillion = Orchestration` 대응을 히어로 강조 요소에 대해 확인. **D-12 확정 이후에만 활성화**한다
- **C11-9 `dark:` 변형 제로** *(v1.14 신설 · R-8.29)* — `app/**`·`components/**`에 **`dark:` + 강조색**(`cobalt`·`vermillion` 및 각 `-dark`) 조합이 **1건이라도 있으면 실패**. 예외는 R-8.32 화이트리스트 등재로만 허용한다(부류 B의 `dark:text-ivory` 등) *(현행 `dark:*-cobalt` **38건** → **실패해야 한다**)*
- [ ] AC-49 *(v1.13 신설 · AC-48 유효성)* **Gate 10·11 폴트 인젝션** — ① F1 `DirectionsBlock:8` 현행 유지 → C11-2 실패, ② F2 `lectures:50`의 `text-xs text-vermillion` 현행 유지 → AC-44 실패(**면제 제거 확인**), ③ F3 다크 `--cobalt-rgb`를 라이트와 동일하게 유지 → AC-45 실패(1.00), ④ F4 `Header:69`에서 `dark:text-ivory` 제거 → AC-43 또는 C11-3 실패(다크 2.85), ⑤ F5 `standardization:89`를 `bg-cobalt text-ivory`로 되돌림 → AC-43 실패(3.45), ⑥ F6 화이트리스트 밖 위치에 `text-white` 1건 추가 → AC-47 실패(**위치 불일치** — 단순 계수로는 통과했을 사례), ⑦ F7 `focus:ring-cobalt/50` 현행 유지 → C11-5 실패(1.80), ⑧ F8 `docs/DESIGN.md` 색상표 hex 1글자 변경 → C11-7 실패, ⑨ F9 실존하지 않는 `(파일, 행)`을 화이트리스트에 등재 → 고아 항목으로 실패, **⑩ F10 `members/page.tsx:108`을 `text-cobalt-dark dark:text-cobalt`로 되돌리면 AC-44 실패(다크 4.37) — 후방탐색 `(?<!dark:)` 제거 확인**, **⑪ F11 `dark:bg-cobalt` 1건을 되살리면 C11-9 실패** *(⑩·⑪은 v1.14 신설)*.
- **11건 전부 실패를 재현하지 못하면 Gate 11을 채택하지 않는다.** F1·F2·F3·F7·**F10·F11**은 **현행 코드 자체가 주입 상태**이므로 게이트 구현 직후 실행만으로 즉시 검증된다
- [ ] AC-50 *(v1.13 신설 · R-8.27 · AC-23)* **`docs/DESIGN.md` 동기화** — 아래 4건을 교정하고 구현과 일치시킨다.
- **§10 커스터마이즈 훅(302~308행)에 대비 제약을 명문화**한다. 현행 안내는 교체 **방법**만 적고 지켜야 할 제약을 적지 않아 `eaddc4f` 사고의 직접 원인으로 규명되었는데, **v1.11에서 지적된 뒤에도 미교정 상태로 남아 같은 유형의 무기록 색 교체가 4회 더 발생**했다
- **§8-1(282행)** 이 Header 활성 배지를 `bg-vermillion text-white dark:text-ivory`로 기술하나 실제는 `bg-cobalt-dark` — 갱신한다
- **§9(292행)** "강조색 위 텍스트는 항상 `ivory`" ↔ 실제 `text-white` 7건 — R-8.26 v1.13 조항(`-dark` 배경은 `ivory` 단독 / DEFAULT 배경만 `white` 예외)으로 서술을 정합화한다
- **§2(30행)** 시맨틱 페어링 ↔ 히어로 `Orchestration` 색 변경 — **D-12 확정 방향에 맞춰** 코드 또는 문서 중 한쪽을 교정한다
- [ ] AC-51 *(v1.14 신설 · R-8.26·R-8.29)* **다크 테마 소형 텍스트 해소** — `dark:text-cobalt`가 `bg-paper` 위 소형 텍스트에 적용되어 다크 테마에서 **4.37**로 미달하는 아래 **8곳**이 `text-cobalt-dark` 단독(다크 **5.96**)으로 교정되었음을 개별 확인한다.
- `app/page.tsx:148`(Pillar 번호, `text-sm`, `bg-paper` @`:142`) · `app/page.tsx:187`(`Pillar #`, `text-xs` @`:183`)
- `app/members/page.tsx:108`(Ph.D. 학위, `bg-paper` @`:102`) · `app/members/page.tsx:138`(M.S. 학위, `bg-paper` @`:132`)
- `app/_components/ProjectPageView.tsx:88`·`:93`(Standards Track / Organization, `text-xs` @`:80`, `bg-paper` @`:44`)
- `app/standardization/page.tsx:36`(`[{scope}]`, `bg-paper` @`:32`) · `app/publications/[category]/page.tsx:124`(등록번호, `text-xs` @`:118`, `bg-paper` @`:103`)
- `bg-ivory` 위 사용처(`ProjectPageView:53` · `members:54,63,202` · `standardization:64` · `DirectionsBlock:51` · 섹션 키커 전량)는 **4.82로 통과하되 여유가 0.32뿐**이므로 함께 `-dark` 단독으로 정리한다(→ R-8.29 · C11-9)
--- ---
@@ -694,8 +771,11 @@ Publications(P4)는 데이터 규모(223건)와 필터·점진적 노출 요구
| **D-07** | Research Project 카드의 **`Funder/Sponsor` 실데이터 확보** 여부 | 🔴 **미결** | `funder` 미노출 + 실존하는 `Standards Track` 표기 | PJ-07. `ISL-2026/` 전체를 `과제·연구비·IITP·NRF·한국연구재단·정보통신기획평가원` 등으로 검색해 **0건**이다. **임의 생성은 MG-05·R-9.17·R-9.18 위반이며 실명·실제 이메일이 게시되는 페이지이므로 절대 불가.** 기본값으로 **구현 차단은 아니며**, 지도교수가 사업명·부처·기간을 제공하면 `content/projects.ts` 재생성만으로 반영된다 | | **D-07** | Research Project 카드의 **`Funder/Sponsor` 실데이터 확보** 여부 | 🔴 **미결** | `funder` 미노출 + 실존하는 `Standards Track` 표기 | PJ-07. `ISL-2026/` 전체를 `과제·연구비·IITP·NRF·한국연구재단·정보통신기획평가원` 등으로 검색해 **0건**이다. **임의 생성은 MG-05·R-9.17·R-9.18 위반이며 실명·실제 이메일이 게시되는 페이지이므로 절대 불가.** 기본값으로 **구현 차단은 아니며**, 지도교수가 사업명·부처·기간을 제공하면 `content/projects.ts` 재생성만으로 반영된다 |
| **D-08** | Research Project 집합의 **정체성 범위** — 레거시 3건(IoT/CCIS/VLC)만인지, ANL 신규 과제를 포함할지 | 🟡 미결 | 레거시 3건 노출, 섹션 라벨 `Research Projects` | 레거시 3건은 IoT 시대 실적이고 현 정체성은 ANL이다. 신규 과제 추가는 지도교수 입력이 필요하다(MG-05). 레거시 3건 비노출은 PR-01에 역행하므로 배제 | | **D-08** | Research Project 집합의 **정체성 범위** — 레거시 3건(IoT/CCIS/VLC)만인지, ANL 신규 과제를 포함할지 | 🟡 미결 | 레거시 3건 노출, 섹션 라벨 `Research Projects` | 레거시 3건은 IoT 시대 실적이고 현 정체성은 ANL이다. 신규 과제 추가는 지도교수 입력이 필요하다(MG-05). 레거시 3건 비노출은 PR-01에 역행하므로 배제 |
| **D-09** | `/publications` 인덱스 라우트를 **Overview 뷰 / `redirect()` / 기본 카테고리 재렌더 / 전체 통합 뷰 + `All` 탭** 중 무엇으로 둘 것인가 | ✅ **재확정** (v1.10, 2026-07-31) | — | **결정: Overview 뷰 채택(A안).** v1.9는 C안(첫 카테고리 본문 재렌더)을 채택했으나, 개발 팀장 이의제기(Job `22b7a309`)대로 **동일 DOM 정적 HTML 2본**이 생성되어 R-8.25를 위반한다 — 지적이 타당하여 채택안을 교체한다. `redirect()`(B안)는 AC-25·AC-40과 충돌하고, `All` 탭 포함 전체 통합 뷰(D안)는 commit `22a31ff`의 탭 제거 결정을 되돌리며 223건 단일 페이지로 P-07·R-9.11과도 충돌한다. A안은 **고유 콘텐츠**(카테고리별 집계 + 최신 하이라이트)를 가지므로 중복이 원천 소멸하고, `All` 탭 없이 3개 탭 체계를 유지해 `22a31ff`의 의도를 보존한다 → IA-03 재개정 · R-8.25 · AC-41 | | **D-09** | `/publications` 인덱스 라우트를 **Overview 뷰 / `redirect()` / 기본 카테고리 재렌더 / 전체 통합 뷰 + `All` 탭** 중 무엇으로 둘 것인가 | ✅ **재확정** (v1.10, 2026-07-31) | — | **결정: Overview 뷰 채택(A안).** v1.9는 C안(첫 카테고리 본문 재렌더)을 채택했으나, 개발 팀장 이의제기(Job `22b7a309`)대로 **동일 DOM 정적 HTML 2본**이 생성되어 R-8.25를 위반한다 — 지적이 타당하여 채택안을 교체한다. `redirect()`(B안)는 AC-25·AC-40과 충돌하고, `All` 탭 포함 전체 통합 뷰(D안)는 commit `22a31ff`의 탭 제거 결정을 되돌리며 223건 단일 페이지로 P-07·R-9.11과도 충돌한다. A안은 **고유 콘텐츠**(카테고리별 집계 + 최신 하이라이트)를 가지므로 중복이 원천 소멸하고, `All` 탭 없이 3개 탭 체계를 유지해 `22a31ff`의 의도를 보존한다 → IA-03 재개정 · R-8.25 · AC-41 |
| **D-10** | `eaddc4f`가 도입한 `--cobalt: #4A7CE8`의 **라이트 테마 대비 미달**(ivory 3.49 / paper 3.16)을 어떻게 해소할 것인가 | ✅ **확정** (v1.11, 2026-07-31) | — | **결정: C안 채택 — `--cobalt`는 #4A7CE8 그대로 두고 `--cobalt-dark`를 `52 98 204`(#3462CC) → `50 84 158`(#32549E)로 재조정한 뒤, 소형 텍스트 사용처를 `-dark` 변형으로 라우팅한다.** 근거: ⓐ **사용자가 지정한 브랜드 색이 보존된다** — 히어로 SVG(`var(--cobalt)` 직접 참조)·대형 세리프 수치·불릿·테두리·아이콘 등 시각적으로 먼저 인지되는 표면은 전부 #4A7CE8을 유지하며, 이들은 대형 텍스트·비텍스트 UI 기준(3:1)을 실측 3.49로 충족한다. ⓑ `cobalt-dark` 슬롯은 **현재 사용처 0건**이므로 신규 토큰 없이 빈 슬롯을 활용해 토큰 계층이 늘지 않는다. ⓒ 결과값 6.43 / 5.82는 `vermillion-dark`(6.38 / 5.77)와 **동일 등급**이라 두 강조색이 대칭이 된다. **기각 사유** — A안(`--cobalt` 자체를 #3C64BC로 하향)은 브랜드 색이 눈에 띄게 어두워져 지시를 훼손하고, B안(신규 `--cobalt-text` 토큰)은 비어 있는 `-dark` 슬롯과 역할이 중복되며, D안(#1B3A8A 복귀)은 사용자 지시 역행이다. **#3C64BC(paper 4.51)가 아니라 #32549E(5.82)를 쓰는 이유**는 4.51이 임계선 대비 여유 0.01에 불과해 §12.1의 `opacity-90` 사고(4.69 → 4.05)처럼 후속 변경 한 번으로 재미달하기 때문이다. **v1.12 개정 — 배지·칩 전경/배경**: 개발 팀장(Agy) 이의제기(Job `7f136931`)를 수용해 v1.11의 `bg-cobalt text-ink dark:text-ivory`(5.03 / 4.82)를 **폐기**하고 **`bg-cobalt-dark text-ivory`(6.43 / 6.57)** 로 교체한다. v1.11은 소형 *텍스트* 12곳에만 `-dark` 라우팅을 적용하고 **동일 크기 등급(10.4 px)의 상태 칩 2곳은 DEFAULT 배경으로 남겨 스스로의 원칙과 어긋나 있었다** — 지적이 타당하다. 다만 이의제기의 권장 전경 `text-white dark:text-ivory`(7.25 / 6.57)는 **미채택** — `-dark` 배경에서는 `ivory` 전경만으로 양 테마가 통과하므로 팔레트 외 색 `white`와 `dark:` 변형이 **모두 불필요**하다(→ R-8.29). 아울러 권장안을 그대로 적용하면 `standardization:87-89` 삼항식의 **나란한 두 칩의 상대휘도 대비가 1.19 → 1.54로 악화**되므로, vermillion 칩도 `-dark`로 통일해 **1.01**로 맞춘다(→ R-8.26 짝 칩 등급 일치 조항) → R-8.26 · R-8.29 · AC-42 · AC-43 · AC-44 · AC-47 | | **D-10** | `eaddc4f`가 도입한 `--cobalt: #4A7CE8`의 **라이트 테마 대비 미달**(ivory 3.49 / paper 3.16)을 어떻게 해소할 것인가 | ✅ **확정** (v1.11, 2026-07-31) | — | **결정: C안 채택 — `--cobalt`는 #4A7CE8 그대로 두고 `--cobalt-dark`를 `52 98 204`(#3462CC) → `50 84 158`(#32549E)로 재조정한 뒤, 소형 텍스트 사용처를 `-dark` 변형으로 라우팅한다.** 근거: ⓐ **사용자가 지정한 브랜드 색이 보존된다** — 히어로 SVG(`var(--cobalt)` 직접 참조)·대형 세리프 수치·불릿·테두리·아이콘 등 시각적으로 먼저 인지되는 표면은 전부 #4A7CE8을 유지하며, 이들은 대형 텍스트·비텍스트 UI 기준(3:1)을 실측 3.49로 충족한다. ⓑ `cobalt-dark` 슬롯은 **현재 사용처 0건**이므로 신규 토큰 없이 빈 슬롯을 활용해 토큰 계층이 늘지 않는다. ⓒ 결과값 6.43 / 5.82는 `vermillion-dark`(6.38 / 5.77)와 **동일 등급**이라 두 강조색이 대칭이 된다. **기각 사유** — A안(`--cobalt` 자체를 #3C64BC로 하향)은 브랜드 색이 눈에 띄게 어두워져 지시를 훼손하고, B안(신규 `--cobalt-text` 토큰)은 비어 있는 `-dark` 슬롯과 역할이 중복되며, D안(#1B3A8A 복귀)은 사용자 지시 역행이다. **#3C64BC(paper 4.51)가 아니라 #32549E(5.82)를 쓰는 이유**는 4.51이 임계선 대비 여유 0.01에 불과해 §12.1의 `opacity-90` 사고(4.69 → 4.05)처럼 후속 변경 한 번으로 재미달하기 때문이다. **v1.12 개정 — 배지·칩 전경/배경**: 개발 팀장(Agy) 이의제기(Job `7f136931`)를 수용해 v1.11의 `bg-cobalt text-ink dark:text-ivory`(5.03 / 4.82)를 **폐기**하고 **`bg-cobalt-dark text-ivory`(6.43 / 6.57)** 로 교체한다. v1.11은 소형 *텍스트* 12곳에만 `-dark` 라우팅을 적용하고 **동일 크기 등급(10.4 px)의 상태 칩 2곳은 DEFAULT 배경으로 남겨 스스로의 원칙과 어긋나 있었다** — 지적이 타당하다. 다만 이의제기의 권장 전경 `text-white dark:text-ivory`(7.25 / 6.57)는 **미채택** — `-dark` 배경에서는 `ivory` 전경만으로 양 테마가 통과하므로 팔레트 외 색 `white`와 `dark:` 변형이 **모두 불필요**하다(→ R-8.29). 아울러 권장안을 그대로 적용하면 `standardization:87-89` 삼항식의 **나란한 두 칩의 상대휘도 대비가 1.19 → 1.54로 악화**되므로, vermillion 칩도 `-dark`로 통일해 **1.01**로 맞춘다(→ R-8.26 짝 칩 등급 일치 조항) **v1.13 개정 — 확정값 갱신**: 커밋 `ca4a7ee`·`8db16e4`가 `--cobalt`를 **#4E77FF**, `--cobalt-dark`를 확정값 #32549E가 아닌 **#345AD2**(`52 90 210`)로 설정했다. #345AD2는 `paper` **4.76** / `ivory` **5.26**으로 R-8.26 바닥값을 **충족**하고 브랜드 색상(226.1°)에도 #32549E보다 가까우므로 **확정값을 #345AD2로 개정**한다. 단 `paper` 여유가 **0.26**뿐이라 위에서 #3C64BC를 배격한 것과 같은 사유가 부분적으로 적용되므로 §12.2 R-4로 등재하고, **추가 상향은 금지**한다(색상 유지 시 `paper` 4.5 한계는 #3B5AC2 4.92 부근이라 이득이 작다). 아울러 **#4E77FF는 반전 대비 1.00으로 R-8.28을 다시 위반**하며 이는 D-10 근본 원인의 재발이다 → **D-12**로 이관 → R-8.26 · R-8.28 · R-8.29 · AC-42 · AC-43 · AC-44 · AC-45 · AC-47 |
| **D-11** | `text-vermillion`(DEFAULT) 소형 텍스트 **약 30곳**을 교정할 것인가 | 🟡 미결 | **현행 유지 (교정하지 않음)** | 실측 `ivory` 4.16 / `paper` 3.77로 R-8.26 소형 텍스트 기준에 미달하나, ⓐ `eaddc4f`가 만든 회귀가 아니라 **최초 커밋 `36a4cd8` 이래의 상태**이고, ⓑ 30곳 일괄 변경은 "전체적인 에디토리얼 디자인 시스템 유지" 목표와 정면 충돌하는 시각적 대변경이며, ⓒ **vermillion(#D9342B)은 사용자가 명시적으로 현행 유지를 지정한 색**이다. 교정 시에는 D-10과 동일한 패턴(`text-vermillion-dark dark:text-vermillion`, 라이트 6.38 / 5.77)을 적용하면 되므로 **기술적 경로는 확보되어 있고 판단만 남는다.** AC-44 화이트리스트에 사유와 함께 등재해 게이트가 이 결정을 명시적으로 기록하게 한다 | | **D-11** | `text-vermillion`(DEFAULT) 소형 텍스트 **약 30곳**을 교정할 것인가 | 🟡 미결 | **현행 유지 (교정하지 않음)** | 실측 `ivory` 4.16 / `paper` 3.77로 R-8.26 소형 텍스트 기준에 미달하나, ⓐ `eaddc4f`가 만든 회귀가 아니라 **최초 커밋 `36a4cd8` 이래의 상태**이고, ⓑ 30곳 일괄 변경은 "전체적인 에디토리얼 디자인 시스템 유지" 목표와 정면 충돌하는 시각적 대변경이며, ⓒ **vermillion(#D9342B)은 사용자가 명시적으로 현행 유지를 지정한 색**이다. 교정 시에는 D-10과 동일한 패턴(`text-vermillion-dark dark:text-vermillion`, 라이트 6.38 / 5.77)을 적용하면 되므로 **기술적 경로는 확보되어 있고 판단만 남는다.** AC-44 화이트리스트에 사유와 함께 등재해 게이트가 이 결정을 명시적으로 기록하게 한다. **v1.13 실측 갱신** — 정확한 건수는 **23건**(상태 변형 포함)이며 최저값은 `bg-paper` 카드 내부의 `members/page.tsx:114,144` **3.77**이다. `Footer.tsx:53,71`의 `hover:text-vermillion`도 `bg-paper` 위라 **3.77**이다 |
| **D-12** | **"Primary Color(#4E77FF) 전 요소 통일 적용"의 판정 기준을 토큰 값으로 볼 것인가, 화면 인지색으로 볼 것인가** — 그리고 히어로 `Orchestration` 강조어의 색을 R-8.5 규약대로 vermillion으로 되돌릴 것인가 | 🔴 미결 (v1.13 신설) | **A안 + B안 병행** | 접근성 제약상 소형 텍스트에는 #4E77FF를 쓸 수 없다(라이트 3.45 / 3.12). 그 결과 **토큰은 #4E77FF, 화면은 #345AD2**라는 이중 상태이며, 라이트 테마에서 #4E77FF가 **정적으로** 칠해지는 곳은 히어로 SVG와 6 px 불릿 **2곳뿐**이다(hover·focus 제외). **A안(현행 유지)**: 코드 변경 0, 지시는 토큰 수준에서 충족. **B안(대형 표면 확대)**: `page.tsx:84,92`의 30 px 지표 등 3:1을 충족하는 **대형 텍스트·도형 표면에 #4E77FF를 적극 확대**해 브랜드 가시성을 회복한다 — 접근성 손실 없이 문제를 실질적으로 완화하는 유일한 경로다. **C안(`-dark` 상향) 기각** — 여유 0.26에서 더 밝히면 D-10이 배격한 임계선 상황에 근접한다. **히어로 `Orchestration`**: `docs/DESIGN.md:30`과 R-8.5가 **vermillion = Orchestration**을 명문 규정하므로, cobalt를 유지하려면 **문서를 먼저 개정**해야 한다. 현재는 코드가 문서를 앞질러 같은 페이지 안에서 규약이 반씩 갈린 상태다. 어느 쪽이든 **코드와 문서가 같은 값을 말해야 한다** → R-8.5 · AC-48(C11-8) · AC-50 |
| **D-13** | `--vermillion`의 **반전 대비 1.25**(라이트 `217 52 43` ↔ 다크 `235 75 66`)를 교정할 것인가 | 🟡 미결 (v1.13 신설) | **현행 유지 + 화이트리스트 등재** | R-8.28 바닥값 2.0에 미달하나 ⓐ **최초 커밋 이래의 상태**로 특정 커밋이 만든 회귀가 아니고, ⓑ **#D9342B는 사용자가 현행 유지를 지정한 Secondary**이며, ⓒ `--vermillion-dark`(2.58)가 정상이라 소형 텍스트 경로는 이미 확보되어 있다. 다만 R-8.28의 취지대로 **"반대 테마 값을 조정하는 순간 양 테마가 동시에 무너지는" 상태**이므로, AC-45 화이트리스트에 **사유와 함께 등재**해 게이트가 이 결정을 명시적으로 기록하게 한다 → R-8.28 · AC-45 |
| **D-14** | `--cobalt` DEFAULT의 **반전 대비 1.00**을 어떻게 처리할 것인가 — R-8.28 예외로 등록할 것인가, 다크 값을 대칭 상향할 것인가 | 🔴 미결 (v1.14 신설, Agy 이의제기 `4962a667` 반영) | **A안 (브랜드 고정 예외)** | R-8.29 정리(강조색 `dark:` 변형 38건 제거) 후 `--cobalt` DEFAULT는 **어느 테마에서도 소형 텍스트를 담지 않으며** 남는 사용처(히어로 SVG·6 px 불릿·hover 테두리·토글 아이콘·대형 텍스트)가 **양 테마에서 3:1을 충족**한다(최저 3.12). 즉 **측정 가능한 실패는 남지 않고 형식적 위반만 남는다.** **A안 — 브랜드 고정(theme-invariant) 토큰으로 등록**: 다크 값을 라이트와 정확히 동일(`78 119 255`)하게 맞춰 불변성을 소스에 드러내고 AC-45 화이트리스트에 등재한다. 근거 ⓐ R-8.28은 *수단*, R-8.26·R-8.30(테마별 대비)이 *목적* 이며 목적 규칙이 양 테마에서 실효 검사되면(AC-43 v1.14 개정) 안전망이 유지된다 — 검증: `#1B3A8A` 역주입 시 다크 지면 대비 **1.80**으로 AC-43 즉시 실패. ⓑ #4E77FF는 **사용자 지정 Primary**이므로 다크 테마만 다른 색으로 치환하면 브랜드 정체성이 갈라진다. **B안 — 대칭 상향(이의제기 구조 채택 · 수치 정정)**: `--cobalt-rgb`(다크) `168 188 255`(#A8BCFF, 반전 **2.09**) + `--cobalt-dark-rgb`(다크) `199 212 255`(#C7D4FF, 반전 **4.04**, 다크 `paper` 11.56 > DEFAULT 9.14로 R-8.33 충족). 이의제기 원안의 `#85ACFF`는 반전 **1.73**으로 **R-8.28 미달**이라 미채택하고, `#B0CDFF`의 기재 반전 2.50은 `#345AD2`의 상대휘도를 폐기값 #32549E의 0.0949로 적은 오기에서 파생된 값으로 실제는 **3.68**이다. B안의 비용은 다크 강조색이 **파스텔로 변해 브랜드 색이 다크 테마에서 사라지고** `bg-cobalt-dark` 칩이 대비 12.75의 거의 흰 배경이 되는 것 — **D-12(브랜드 가시성)를 다크 테마에서 악화**시킨다. **A안은 본 계획자가 작성한 R-8.28을 스스로 완화하는 제안이므로 cline·GM의 별도 심사를 요청**하며, 승인 전에는 집행하지 않는다 → R-8.28 · R-8.29 · R-8.33 · AC-43 · AC-45 |
#### D-01 상세 #### D-01 상세
@@ -873,6 +953,34 @@ cobalt 칩만 어둡게 내리면 vermillion 칩이 밝은 채로 남아 무게
**시각 변경 고지.** 부류 A의 vermillion 칩 2곳은 배경이 **#D9342B → #A8231C로 실제로 어두워진다.** `#A8231C`는 팔레트에 이미 정의된 `vermillion-dark`이므로 신규 색이 아니며(R-8.1 위반 아님) 대상 면적도 `text-[0.65rem]` 칩 2개로 극히 작다. **GM이 배경 무변경을 선호하면 부류 B 패턴(`bg-vermillion text-white dark:text-ivory`, 4.69)으로 내려도 규격은 충족**되며, 그 경우 위 표의 무게 불균형 1.54가 남는다는 점만 함께 기록한다 — **차단 사유가 아니라 선택지다.** **시각 변경 고지.** 부류 A의 vermillion 칩 2곳은 배경이 **#D9342B → #A8231C로 실제로 어두워진다.** `#A8231C`는 팔레트에 이미 정의된 `vermillion-dark`이므로 신규 색이 아니며(R-8.1 위반 아님) 대상 면적도 `text-[0.65rem]` 칩 2개로 극히 작다. **GM이 배경 무변경을 선호하면 부류 B 패턴(`bg-vermillion text-white dark:text-ivory`, 4.69)으로 내려도 규격은 충족**되며, 그 경우 위 표의 무게 불균형 1.54가 남는다는 점만 함께 기록한다 — **차단 사유가 아니라 선택지다.**
#### D-12 상세 — 게이트가 녹색인 채로 남은 결함과 브랜드 색의 가시성 *(v1.13)*
**관측.** `eaddc4f` → `082d71e` 4개 커밋 이후 상태에서 `npm run build`(exit 0 · 13/13 정적 · `ƒ Dynamic` 0건)와 `run_e2e_tests.py` **Gate 0~10 전량**이 통과한다. 그러나 같은 상태에서 **WCAG AA 미달 6건과 통일성 결함 9건**이 실재한다. 특히 `lectures/page.tsx:89`는 **v1.12 AC-42 부류 B에 "미교정"으로 명시 등재된 항목**인데 신설된 대비 게이트가 이를 통과시켰다.
**원인 — 게이트의 범위 결함 3종.**
| ID | 결함 | 결과 |
| :--- | :--- | :--- |
| G-1 | AC-44 구현의 면제 조항 `if … or "text-vermillion" in line: continue`. 위반 판정 정규식이 `text-(cobalt\|vermillion)`이므로 **vermillion으로 매치된 라인은 예외 없이 그 문자열을 포함**한다 | **AC-44의 vermillion 절반이 영구 무효.** 실제 소스 4개 라인 + 주입 2건을 동일 로직에 투입해 전부 면제됨을 재현 |
| G-2 | AC-45 구현이 `cobalt-dark`·`vermillion-dark` **2종만** 검사 | **`--cobalt` 반전 대비 1.00 미검출** — D-10 근본 원인의 재발 |
| G-3 | AC-43 구현이 토큰 값만 보고 **소스의 전경/배경 쌍은 보지 않음** | `bg-cobalt-dark text-white`(다크 2.85)를 작성해도 통과 |
G-1은 **N6**(Gate 8 C1/C2 공허 단정, 10라운드 미해소)과 **동일 유형**이다 — 단정·면제가 구조적으로 발화하지 않는데 출력은 ✅로 찍힌다. 개별 수정이 아니라 형식 차원의 금지(**R-8.32**)로 대응한다. 아울러 §12.1 D-09(알파 계약)·D-10(반전 계약)에 이어 `focus:ring-cobalt/50` **1.80** · `hover:border-cobalt/60` **1.95**가 드러났다 — **알파·토큰 조합이 무성으로 대비를 무너뜨리는 유형의 세 번째 발현**이며 **R-8.30**의 근거다.
**브랜드 색의 가시성.** 라이트 테마에서 지정 Primary **#4E77FF가 정적으로 칠해지는 표면은 히어로 SVG와 6 px 불릿 2곳뿐**이다. 나머지는 전부 hover·focus 상태이거나 `-dark` **#345AD2**다.
| 표면 | 색 | 상태 |
| :--- | :--- | :--- |
| 히어로 `HeroComposition` SVG (`var(--cobalt)`) | **#4E77FF** | 정적 |
| Pillar 불릿 `page.tsx:174` `bg-cobalt` (6 px) | **#4E77FF** | 정적 |
| `members:102,132` `hover:border-cobalt` · `ProjectPageView:35,44` | #4E77FF | hover/focus 전용 |
| `ThemeToggle:146` `text-cobalt` | #4E77FF | **다크 테마 전용 렌더** |
| 섹션 괘선·키커·링크·지표·Pillar 라벨(20여 곳) | **#345AD2** | 정적 — 사용자가 "Primary"로 인지하는 색 |
즉 **지시는 토큰 수준에서 충족되었으나 화면 수준에서는 다른 색이 Primary로 읽힌다.** 접근성을 지키면서 이 간극을 좁히는 유일한 경로가 **B안(대형 표면 확대)** 이다 — `page.tsx:84,92`의 30 px 지표처럼 **3:1만 요구되는 표면**은 #4E77FF로 칠해도 적법하다(실측 3.45). 반대로 `-dark`를 브랜드 색 쪽으로 밝히는 C안은 `paper` 여유가 0.26뿐이라 **D-10이 #3C64BC를 배격한 것과 같은 사유**로 기각한다.
**시맨틱 규약의 분열.** `docs/DESIGN.md:30`과 R-8.5는 **vermillion = Agent Eng / Orchestration**을 규정한다. `2441f35`가 히어로의 `Orchestration` 강조어를 cobalt로 바꾸었으나, 같은 파일의 PILLARS 교차 배정(`page.tsx:12~48`)은 규약을 유지 중이다 — **한 페이지 안에서 규약이 둘로 갈렸다.** 코드를 되돌리든 문서를 개정하든 무방하나, **둘 중 하나는 반드시 상대에 맞춰야** AC-23이 성립한다.
--- ---
### 12.2 리스크 ### 12.2 리스크
@@ -888,6 +996,10 @@ cobalt 칩만 어둡게 내리면 vermillion 칩이 밝은 채로 남아 무게
| R-05 | 잔재 검사(MG-08)가 논문 제목의 도메인 용어까지 오검출 | **실데이터를 잔재로 오인해 삭제** → PR-01 위반 | MG-08 예외 규정을 검사 스크립트에 반영. 검사 범위를 정체성 서술 영역으로 한정 | | R-05 | 잔재 검사(MG-08)가 논문 제목의 도메인 용어까지 오검출 | **실데이터를 잔재로 오인해 삭제** → PR-01 위반 | MG-08 예외 규정을 검사 스크립트에 반영. 검사 범위를 정체성 서술 영역으로 한정 |
| R-06 | 문서-코드 불일치 재발 | 후속 작업자 혼선 | R-9.19. 미완 영역은 문서에 미완으로 표기 | | R-06 | 문서-코드 불일치 재발 | 후속 작업자 혼선 | R-9.19. 미완 영역은 문서에 미완으로 표기 |
| R-07 | P2(Header 수정)와 P3~P5 병렬 진행 시 충돌 | 머지 충돌 | §11.1 주석대로 P2 선행 완료 | | R-07 | P2(Header 수정)와 P3~P5 병렬 진행 시 충돌 | 머지 충돌 | §11.1 주석대로 P2 선행 완료 |
| R-10 *(v1.13 신설)* | 색값 교체가 **대비 재산정표 없이** 5회 연속 진행됨(`eaddc4f`·`ca4a7ee`·`8db16e4`·`2441f35`·`082d71e` 전부 커밋 본문 없음) | 관행화 시 무성 실패가 반복 발생 | R-8.1·R-8.27 이행 강제 + **AC-50**으로 `docs/DESIGN.md` 커스터마이즈 훅 교정. 문서가 원인으로 규명된 지 2라운드째 미교정 |
| R-11 *(v1.13 신설)* | **게이트 통과가 결함 부재의 근거로 오용될 위험** — Gate 0~10 전량 ✅인 상태에서 AA 미달 6건이 실재했다 | 잘못된 PASS 확정 | 폴트 인젝션 없는 게이트는 채택하지 않는다(**AC-46 · AC-49**). 신설 게이트는 **현행 코드 자체가 주입 상태**인 항목(F1·F2·F3·F7)으로 즉시 실효성을 확인한다 |
| R-12 *(v1.13 신설)* | `--cobalt-dark` #345AD2의 `paper` 여유가 **0.26**뿐 | 후속 변경 1회로 재미달 | 추가 상향 금지(D-10 v1.13 개정). 변경 시 R-8.27 재산정 필수 |
| R-13 *(v1.13 신설)* | Phase 3 색 통일 작업이 "에디토리얼 디자인 시스템 유지" 목표와 충돌할 소지 | 시각적 대변경 | 단일화 대상을 **동일 역할 시그니처 내부**로 한정(R-8.31). 색 선택 자체는 **D-12**로 이관 |
--- ---
@@ -935,3 +1047,5 @@ cobalt 칩만 어둡게 내리면 vermillion 칩이 밝은 채로 남아 무게
| 1.10 | 2026-07-31 | claude (planner) | 개발 팀장(Agy) 기술 이의제기(Job `22b7a309`) 반영. **D-09 재확정** — v1.9의 C안(첫 카테고리 본문 재렌더)이 **동일 DOM 정적 HTML 2본**을 만든다는 지적이 타당하여 **A안(Overview 뷰)** 으로 교체하고, `All` 탭 재도입(D안)은 commit `22a31ff` 결정 역행·P-07 충돌로 기각. **R-8.25 신설**(라우트 간 콘텐츠 중복 금지 + canonical 예외 절차), **AC-41 신설**(`<main>` 정규화 해시 중복 0건), **IA-03 재개정**. 이의제기의 `isActive` 논거는 정정 — `pathname.startsWith(\`${href}/\`)` 일반 규칙이 하위 카테고리를 이미 포괄하므로 모호성이 없음을 코드로 확인. 아울러 별건으로 `CategoryNav` 활성 탭(`bg-vermillion text-ivory`)이 **라이트 테마 4.16:1로 R-9.6 미달**임을 발견해 **AC-42 신설**. Job `6a6094c6` | | 1.10 | 2026-07-31 | claude (planner) | 개발 팀장(Agy) 기술 이의제기(Job `22b7a309`) 반영. **D-09 재확정** — v1.9의 C안(첫 카테고리 본문 재렌더)이 **동일 DOM 정적 HTML 2본**을 만든다는 지적이 타당하여 **A안(Overview 뷰)** 으로 교체하고, `All` 탭 재도입(D안)은 commit `22a31ff` 결정 역행·P-07 충돌로 기각. **R-8.25 신설**(라우트 간 콘텐츠 중복 금지 + canonical 예외 절차), **AC-41 신설**(`<main>` 정규화 해시 중복 0건), **IA-03 재개정**. 이의제기의 `isActive` 논거는 정정 — `pathname.startsWith(\`${href}/\`)` 일반 규칙이 하위 카테고리를 이미 포괄하므로 모호성이 없음을 코드로 확인. 아울러 별건으로 `CategoryNav` 활성 탭(`bg-vermillion text-ivory`)이 **라이트 테마 4.16:1로 R-9.6 미달**임을 발견해 **AC-42 신설**. Job `6a6094c6` |
| 1.11 | 2026-07-31 | claude (planner) | 강조색 교체 커밋 `eaddc4f` 검증 및 리뷰 수렴. **라이트 테마 cobalt 대비가 9.23:1 → 3.49:1(ivory) / 8.35 → 3.16(paper)로 붕괴**하여 소형 텍스트 **14개 사용처**가 AA 미달임을 WCAG 공식 재계산으로 확정. 근본 원인은 **명암 반전 계약 이탈**(라이트↔다크 상대휘도 대비 3.13 → **1.01**)이며, `run_e2e_tests.py`에 대비 검사가 **0건**이라 게이트 0~9와 빌드가 전부 통과한 채로 통과했다 — §12.1 알파 계약 사고와 동일한 무성 실패 유형의 **두 번째 발현**. **R-8.26~R-8.28 신설**(강조색 대비 바닥값 · 색값 변경 시 재측정 의무 · 명암 반전 계약 ≥2.0), **AC-43~AC-46 신설**(대비 게이트 Gate 10 · 소형 텍스트 라우팅 · 반전 계약 · 폴트 인젝션 4종). **D-10 확정** — `--cobalt`는 사용자 지정 #4A7CE8을 **유지**하고 `--cobalt-dark`만 #3462CC → **#32549E**(paper 4.48 → 5.82)로 재조정해 소형 텍스트를 `-dark`로 라우팅(A·B·D안 기각). **D-11 신설**(🟡 `text-vermillion` 소형 텍스트 30여 곳, 기본값 현행 유지). **AC-42 개정** — 직전 수렴에서 통과 판정되었으나 `standardization:40,88`·`lectures:89`·`members:177` **4곳이 4.16:1로 미교정** 잔존함을 확인하고 대상을 명시 목록화, `text-white` on vermillion 수치를 4.58 → **4.69**로 정정. §8.1 색상 토큰표를 실제 값과 동기화하고, **Secondary #D9342B는 최초 커밋 이래 무변경(no-op)** 임을 계보로 확인해 기록. Job `5c2137c6` | | 1.11 | 2026-07-31 | claude (planner) | 강조색 교체 커밋 `eaddc4f` 검증 및 리뷰 수렴. **라이트 테마 cobalt 대비가 9.23:1 → 3.49:1(ivory) / 8.35 → 3.16(paper)로 붕괴**하여 소형 텍스트 **14개 사용처**가 AA 미달임을 WCAG 공식 재계산으로 확정. 근본 원인은 **명암 반전 계약 이탈**(라이트↔다크 상대휘도 대비 3.13 → **1.01**)이며, `run_e2e_tests.py`에 대비 검사가 **0건**이라 게이트 0~9와 빌드가 전부 통과한 채로 통과했다 — §12.1 알파 계약 사고와 동일한 무성 실패 유형의 **두 번째 발현**. **R-8.26~R-8.28 신설**(강조색 대비 바닥값 · 색값 변경 시 재측정 의무 · 명암 반전 계약 ≥2.0), **AC-43~AC-46 신설**(대비 게이트 Gate 10 · 소형 텍스트 라우팅 · 반전 계약 · 폴트 인젝션 4종). **D-10 확정** — `--cobalt`는 사용자 지정 #4A7CE8을 **유지**하고 `--cobalt-dark`만 #3462CC → **#32549E**(paper 4.48 → 5.82)로 재조정해 소형 텍스트를 `-dark`로 라우팅(A·B·D안 기각). **D-11 신설**(🟡 `text-vermillion` 소형 텍스트 30여 곳, 기본값 현행 유지). **AC-42 개정** — 직전 수렴에서 통과 판정되었으나 `standardization:40,88`·`lectures:89`·`members:177` **4곳이 4.16:1로 미교정** 잔존함을 확인하고 대상을 명시 목록화, `text-white` on vermillion 수치를 4.58 → **4.69**로 정정. §8.1 색상 토큰표를 실제 값과 동기화하고, **Secondary #D9342B는 최초 커밋 이래 무변경(no-op)** 임을 계보로 확인해 기록. Job `5c2137c6` |
| 1.12 | 2026-07-31 | claude (planner) | 개발 팀장(Agy) 기술 이의제기(Job `7f136931`) 반영. **D-10 배지·칩 결정 재확정** — v1.11의 `bg-cobalt text-ink dark:text-ivory`(5.03 / 4.82)는 채택안 C의 원칙(작은 요소는 `-dark` 라우팅)을 **소형 텍스트 12곳에만 적용하고 동일 크기 등급의 상태 칩 2곳에는 적용하지 않은 일관성 결함**이었음을 인정하고 폐기, **`bg-{accent}-dark text-ivory`** 로 교체(6.38~6.43 / 6.57~6.72). 이의제기의 권장 전경 `text-white dark:text-ivory`(7.25)는 **미채택** — `-dark` 배경에서는 `ivory`만으로 양 테마가 통과해 **팔레트 외 색 `white`와 `dark:` 변형이 모두 불필요**하다(**R-8.29 신설**). 아울러 권장안을 그대로 적용하면 `standardization:87-89` 삼항식의 나란한 두 칩 상대휘도 대비가 **1.19 → 1.54로 악화**됨을 실측해, vermillion 칩도 `-dark`로 통일하여 **1.01** 균형을 달성(**R-8.26에 배지·칩 라우팅 및 짝 칩 등급 일치 조항 추가**). 배지류를 **부류 A(`text-[0.65rem]` 상태 칩 4곳) / 부류 B(인터랙티브 내비·토글 5곳)** 로 분리해 **AC-42 개정**, `-dark`가 **배경**으로 쓰이는 조합을 **AC-43 검사 대상에 추가**(v1.11 목록은 전경만 다루어 칩 배경을 놓쳤다), **AC-47 신설**(팔레트 외 색 상한 8건 화이트리스트), **AC-46에 폴트 인젝션 ⑤·⑥ 추가**(폐기된 v1.11 칩 패턴 및 `white` 증가 검출). 부류 A vermillion 칩 2곳의 배경이 #D9342B → #A8231C로 어두워지는 **시각 변경은 GM 승인 사항으로 명시**하되 차단 사유가 아님을 기록. Job `3d3b823a` | | 1.12 | 2026-07-31 | claude (planner) | 개발 팀장(Agy) 기술 이의제기(Job `7f136931`) 반영. **D-10 배지·칩 결정 재확정** — v1.11의 `bg-cobalt text-ink dark:text-ivory`(5.03 / 4.82)는 채택안 C의 원칙(작은 요소는 `-dark` 라우팅)을 **소형 텍스트 12곳에만 적용하고 동일 크기 등급의 상태 칩 2곳에는 적용하지 않은 일관성 결함**이었음을 인정하고 폐기, **`bg-{accent}-dark text-ivory`** 로 교체(6.38~6.43 / 6.57~6.72). 이의제기의 권장 전경 `text-white dark:text-ivory`(7.25)는 **미채택** — `-dark` 배경에서는 `ivory`만으로 양 테마가 통과해 **팔레트 외 색 `white`와 `dark:` 변형이 모두 불필요**하다(**R-8.29 신설**). 아울러 권장안을 그대로 적용하면 `standardization:87-89` 삼항식의 나란한 두 칩 상대휘도 대비가 **1.19 → 1.54로 악화**됨을 실측해, vermillion 칩도 `-dark`로 통일하여 **1.01** 균형을 달성(**R-8.26에 배지·칩 라우팅 및 짝 칩 등급 일치 조항 추가**). 배지류를 **부류 A(`text-[0.65rem]` 상태 칩 4곳) / 부류 B(인터랙티브 내비·토글 5곳)** 로 분리해 **AC-42 개정**, `-dark`가 **배경**으로 쓰이는 조합을 **AC-43 검사 대상에 추가**(v1.11 목록은 전경만 다루어 칩 배경을 놓쳤다), **AC-47 신설**(팔레트 외 색 상한 8건 화이트리스트), **AC-46에 폴트 인젝션 ⑤·⑥ 추가**(폐기된 v1.11 칩 패턴 및 `white` 증가 검출). 부류 A vermillion 칩 2곳의 배경이 #D9342B → #A8231C로 어두워지는 **시각 변경은 GM 승인 사항으로 명시**하되 차단 사유가 아님을 기록. Job `3d3b823a` |
| 1.13 | 2026-07-31 | claude (planner) | 테마 색상 통일성 검증 계획 수립 (`eaddc4f`→`082d71e` 4개 커밋). **Gate 0~10 전량 통과·빌드 exit 0인 상태에서 WCAG AA 미달 6건·통일성 결함 9건이 실재**함을 실측으로 확정 — 게이트 통과가 곧 오탐 신호였다. 원인은 게이트 **범위 결함 3종**: ⓐ **AC-44의 vermillion 절반이 구조적으로 무효**(면제 조항 `"text-vermillion" in line`이 판정 정규식과 항상 동시 매치 — 실소스 4라인 + 주입 2건으로 재현), ⓑ AC-45가 `-dark` 2종만 검사해 **`--cobalt` 반전 대비 1.00**(D-10 근본 원인의 재발)을 미검출, ⓒ AC-43이 소스의 전경/배경 쌍을 보지 않아 `bg-cobalt-dark text-white`(다크 2.85)를 통과. **R-8.30 신설**(비텍스트 UI 3:1 · 알파는 합성 후 산정 — `focus:ring-cobalt/50` **1.80** · `hover:border-cobalt/60` **1.95** 검출), **R-8.31 신설**(역할 시그니처 단위 색 통일 — 괘선 6:1 · 키커 11:1 · 배지 전경 6:4 · hover 테두리 4종 · 포커스 2종, 지도교수 이메일 1값이 3색 3대비), **R-8.32 신설**(게이트 면제는 `(파일,행,사유)` 등재만 허용 · 라인 부분문자열 면제 금지 — **N6과 동일 유형**). **R-8.5·R-8.26·R-8.28 개정**, **AC-42·43·44·45·47 개정**, **AC-48~AC-50 신설**(Gate 11 C11-1~8 · 폴트 인젝션 F1~F9 · `docs/DESIGN.md` 동기화 4건). **§8.1 색상표를 실제 토큰값 `#4E77FF`/`#345AD2`로 동기화**하고 **D-10 확정값을 #345AD2로 개정**(paper 4.76, 여유 0.26 → R-12). **D-12 신설**(🔴 Primary 적용의 판정 기준 — 라이트 테마에서 #4E77FF가 정적으로 보이는 곳이 **2곳뿐**이며 인지색은 #345AD2. 히어로 `Orchestration`이 R-8.5·DESIGN.md §2를 위반해 cobalt로 변경되어 한 페이지 안에서 규약이 분열), **D-13 신설**(🟡 `--vermillion` 반전 1.25). **A-1 `lectures:89` 4.16 미교정 잔존**·**A-2 `not-found.tsx:22` 4.16 신규 발견**(어떤 AC에도 없던 항목)·**A-7 `::selection` 4.16 미등재** 확정. **D-11 실측 갱신**(23건, 최저 3.77). 색 교체 5회 연속 대비표 미첨부(R-10). Job `43f3f74a` |
| 1.14 | 2026-07-31 | claude (planner) | 개발 팀장(Agy) 기술 이의제기(Job `4962a667`) 반영. **구조적 통찰 채택** — 다크 `--cobalt`만 단독 상향하면 다크 지면 대비가 DEFAULT 9.2 > `-dark` 5.96으로 **계층이 역전**됨을 실측 확인, v1.13 §7 각주의 누락을 인정하고 **R-8.33 신설**(토큰 등급 불변식). 다만 불변식을 **절대 휘도 순서가 아니라 테마 상대 대비**로 정식화 — 절대 순서의 테마 간 뒤집힘은 vermillion 쌍에서도 동일한 **정상 동작**이며 현행 8개 셀 전부에서 성립하므로, 절대 휘도로 판정하면 정상 상태를 오판한다. **제안 수치 미채택** — 주 권장값 `#85ACFF`의 반전 대비는 **1.73**으로 조정을 촉발한 R-8.28(≥2.0) 자체를 충족하지 못하며, 제안 표의 `#345AD2` L=0.095는 **폐기된 #32549E의 값**(실제 0.1270)이라 `#B0CDFF` 반전 대비 2.50도 실제 **3.68**이다. **이의제기 지점에서 더 큰 결함 발굴** — 다크 `--cobalt`(#4D77FF)는 `bg-paper` 위 **4.37**이므로 v1.12가 지시한 `text-cobalt-dark dark:text-cobalt` 라우팅이 **다크 절반을 DEFAULT로 되돌려 8곳이 AA 미달**이다(**AC-51 신설**). 원인은 R-8.26의 "`paper` 기준 산정" 원칙을 **본 계획자가 라이트 테마에만 적용한 오류**로, v1.11 §6.5와 같은 형태의 누락이다. 게이트도 AC-44 정규식의 **부정 후방탐색 `(?<!dark:)`로 다크 절반을 설계상 면제**하고 있었다(**G-7** — G-1·G-2·G-3에 이은 네 번째 "검사를 절반만 한다" 유형). **처방을 토큰 조정에서 `dark:` 변형 제거로 재설계** — `-dark`는 반전 토큰이므로 `dark:` 없이 4개 셀 전부(5.26/4.76/6.57/5.96)를 통과한다. **R-8.29 강화**(강조색 `dark:` 금지 · 정본 패턴 3종 · 현행 위반 **38건**), **R-8.26·AC-43·AC-44·AC-48(C11-9)·AC-49(F10·F11, 총 11건) 개정**. **D-14 신설**(🔴 `--cobalt` 반전 1.00 — A안 브랜드 고정 예외 권고 / B안 대칭 상향 정정 수치 `#A8BCFF`+`#C7D4FF`). A안은 본 계획자가 작성한 R-8.28의 완화를 포함하므로 **cline·GM 별도 심사 대상**으로 명시했고, 안전망 유지 근거로 `#1B3A8A` 역주입 시 AC-43 즉시 실패(1.80)를 제시했다. Job `d35bc3e7` |
@@ -4,8 +4,8 @@ export function DirectionsBlock() {
return ( return (
<section className="mt-20 pt-16 border-t border-line" id="directions"> <section className="mt-20 pt-16 border-t border-line" id="directions">
<div className="flex items-center gap-3 mb-6"> <div className="flex items-center gap-3 mb-6">
<span className="h-px w-8 bg-vermillion"></span> <span className="h-px w-8 bg-cobalt-dark"></span>
<span className="font-mono text-xs tracking-widest uppercase text-vermillion font-bold"> <span className="font-mono text-xs tracking-widest uppercase text-cobalt-dark font-bold">
Location & Contact Location & Contact
</span> </span>
</div> </div>
@@ -48,7 +48,7 @@ export function DirectionsBlock() {
<span className="font-mono text-xs uppercase tracking-wider text-ink-mute w-20 pt-0.5"> <span className="font-mono text-xs uppercase tracking-wider text-ink-mute w-20 pt-0.5">
관련 기관 관련 기관
</span> </span>
<div className="flex flex-wrap gap-x-4 gap-y-1 text-xs font-mono text-cobalt-dark dark:text-cobalt"> <div className="flex flex-wrap gap-x-4 gap-y-1 text-xs font-mono text-cobalt-dark">
<a href="http://www.knu.ac.kr/" target="_blank" rel="noreferrer" className="hover:underline"> <a href="http://www.knu.ac.kr/" target="_blank" rel="noreferrer" className="hover:underline">
경북대학교 (KNU) ↗ 경북대학교 (KNU) ↗
</a> </a>
@@ -11,8 +11,8 @@ export function ProjectPageView() {
<div className="flex flex-col sm:flex-row sm:items-end justify-between gap-4 border-b border-line pb-4"> <div className="flex flex-col sm:flex-row sm:items-end justify-between gap-4 border-b border-line pb-4">
<div> <div>
<div className="flex items-center gap-3 mb-1"> <div className="flex items-center gap-3 mb-1">
<span className="h-px w-8 bg-cobalt-dark dark:bg-cobalt"></span> <span className="h-px w-8 bg-cobalt-dark"></span>
<span className="font-mono text-xs tracking-widest uppercase text-cobalt-dark dark:text-cobalt font-bold"> <span className="font-mono text-xs tracking-widest uppercase text-cobalt-dark font-bold">
Key Initiatives Key Initiatives
</span> </span>
</div> </div>
@@ -50,7 +50,7 @@ export function ProjectPageView() {
{proj.title} {proj.title}
</h3> </h3>
{proj.period && ( {proj.period && (
<span className="font-mono text-xs text-cobalt-dark dark:text-cobalt bg-ivory border border-line px-2 py-0.5 rounded shrink-0"> <span className="font-mono text-xs text-cobalt-dark bg-ivory border border-line px-2 py-0.5 rounded shrink-0">
{proj.period} {proj.period}
</span> </span>
)} )}
@@ -85,12 +85,12 @@ export function ProjectPageView() {
) : proj.standardsTrack ? ( ) : proj.standardsTrack ? (
<div className="flex items-baseline justify-between gap-2"> <div className="flex items-baseline justify-between gap-2">
<span className="text-ink-mute">Standards Track:</span> <span className="text-ink-mute">Standards Track:</span>
<span className="text-cobalt-dark dark:text-cobalt font-bold">{proj.standardsTrack}</span> <span className="text-cobalt-dark font-bold">{proj.standardsTrack}</span>
</div> </div>
) : proj.standardsOrg ? ( ) : proj.standardsOrg ? (
<div className="flex items-baseline justify-between gap-2"> <div className="flex items-baseline justify-between gap-2">
<span className="text-ink-mute">Organization:</span> <span className="text-ink-mute">Organization:</span>
<span className="text-cobalt-dark dark:text-cobalt font-bold">{proj.standardsOrg}</span> <span className="text-cobalt-dark font-bold">{proj.standardsOrg}</span>
</div> </div>
) : null} ) : null}
+5 -5
View File
@@ -10,8 +10,8 @@ export default function LecturesPage() {
{/* Header */} {/* Header */}
<section className="space-y-3"> <section className="space-y-3">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<span className="h-px w-8 bg-cobalt-dark dark:bg-cobalt"></span> <span className="h-px w-8 bg-cobalt-dark"></span>
<span className="font-mono text-xs tracking-widest uppercase text-cobalt-dark dark:text-cobalt font-bold"> <span className="font-mono text-xs tracking-widest uppercase text-cobalt-dark font-bold">
Academic Courses Academic Courses
</span> </span>
</div> </div>
@@ -27,7 +27,7 @@ export default function LecturesPage() {
<section className="space-y-6 pt-6 md:pt-10 border-t border-line"> <section className="space-y-6 pt-6 md:pt-10 border-t border-line">
<div className="flex justify-between items-end"> <div className="flex justify-between items-end">
<div> <div>
<div className="font-mono text-xs text-cobalt-dark dark:text-cobalt uppercase tracking-widest font-bold"> <div className="font-mono text-xs text-cobalt-dark uppercase tracking-widest font-bold">
Recent Courses ({recentSemesters.length} Semesters) Recent Courses ({recentSemesters.length} Semesters)
</div> </div>
<h2 className="font-serif text-3xl font-normal text-ink mt-1"> <h2 className="font-serif text-3xl font-normal text-ink mt-1">
@@ -47,7 +47,7 @@ export default function LecturesPage() {
<span className="font-serif text-xl font-normal text-ink"> <span className="font-serif text-xl font-normal text-ink">
{sem.term} {sem.year} {sem.term} {sem.year}
</span> </span>
<span className="font-mono text-xs text-vermillion font-bold"> <span className="font-mono text-xs text-vermillion-dark font-bold">
Latest Latest
</span> </span>
</div> </div>
@@ -86,7 +86,7 @@ export default function LecturesPage() {
2016 Fall ~ 2024 Spring 학기별 개설 교과목 이력 2016 Fall ~ 2024 Spring 학기별 개설 교과목 이력
</p> </p>
</div> </div>
<span className="font-mono text-xs text-vermillion font-bold border border-vermillion px-3 py-1.5 rounded group-open:bg-vermillion group-open:text-ivory transition-colors"> <span className="font-mono text-xs text-vermillion-dark font-bold border border-vermillion-dark px-3 py-1.5 rounded group-open:bg-vermillion-dark group-open:text-white dark:group-open:text-ivory transition-colors">
<span className="group-open:hidden">전체 보기 ▾</span> <span className="group-open:hidden">전체 보기 ▾</span>
<span className="hidden group-open:inline">접기 ▴</span> <span className="hidden group-open:inline">접기 ▴</span>
</span> </span>
+12 -12
View File
@@ -15,8 +15,8 @@ export default function MembersPage() {
{/* Header */} {/* Header */}
<section className="space-y-3"> <section className="space-y-3">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<span className="h-px w-8 bg-cobalt-dark dark:bg-cobalt"></span> <span className="h-px w-8 bg-cobalt-dark"></span>
<span className="font-mono text-xs tracking-widest uppercase text-cobalt-dark dark:text-cobalt font-bold"> <span className="font-mono text-xs tracking-widest uppercase text-cobalt-dark font-bold">
People &amp; Researchers People &amp; Researchers
</span> </span>
</div> </div>
@@ -30,7 +30,7 @@ export default function MembersPage() {
{/* Professor Section */} {/* Professor Section */}
<section className="space-y-6 pt-6 md:pt-10 border-t border-line"> <section className="space-y-6 pt-6 md:pt-10 border-t border-line">
<div className="font-mono text-xs text-cobalt-dark dark:text-cobalt uppercase tracking-widest font-bold"> <div className="font-mono text-xs text-cobalt-dark uppercase tracking-widest font-bold">
Faculty Faculty
</div> </div>
@@ -51,7 +51,7 @@ export default function MembersPage() {
<div className="pt-4 border-t border-line/60 flex flex-wrap gap-4 text-xs font-mono"> <div className="pt-4 border-t border-line/60 flex flex-wrap gap-4 text-xs font-mono">
<a <a
href="mailto:sjkoh@knu.ac.kr" href="mailto:sjkoh@knu.ac.kr"
className="text-cobalt-dark dark:text-cobalt hover:underline" className="text-cobalt-dark hover:underline"
> >
sjkoh@knu.ac.kr sjkoh@knu.ac.kr
</a> </a>
@@ -60,7 +60,7 @@ export default function MembersPage() {
href="https://github.com/sjkoh12" href="https://github.com/sjkoh12"
target="_blank" target="_blank"
rel="noreferrer" rel="noreferrer"
className="text-cobalt-dark dark:text-cobalt hover:underline" className="text-cobalt-dark hover:underline"
> >
GitHub @sjkoh12 ↗ GitHub @sjkoh12 ↗
</a> </a>
@@ -81,7 +81,7 @@ export default function MembersPage() {
<section className="space-y-8 pt-6 md:pt-10 border-t border-line"> <section className="space-y-8 pt-6 md:pt-10 border-t border-line">
<div className="flex justify-between items-end"> <div className="flex justify-between items-end">
<div> <div>
<div className="font-mono text-xs text-cobalt-dark dark:text-cobalt uppercase tracking-widest font-bold"> <div className="font-mono text-xs text-cobalt-dark uppercase tracking-widest font-bold">
Active Researchers ({activeMembers.length} Members) Active Researchers ({activeMembers.length} Members)
</div> </div>
<h2 className="font-serif text-3xl font-normal text-ink mt-1"> <h2 className="font-serif text-3xl font-normal text-ink mt-1">
@@ -105,13 +105,13 @@ export default function MembersPage() {
<span className="font-serif text-lg font-normal text-ink"> <span className="font-serif text-lg font-normal text-ink">
{mem.ko} {mem.ko}
</span> </span>
<span className="font-mono text-xs text-cobalt-dark dark:text-cobalt"> <span className="font-mono text-xs text-cobalt-dark">
{mem.degree} {mem.degree}
</span> </span>
</div> </div>
<div className="font-mono text-xs text-ink-mute">{mem.en}</div> <div className="font-mono text-xs text-ink-mute">{mem.en}</div>
{mem.affiliation && ( {mem.affiliation && (
<div className="text-xs text-vermillion pt-1 border-t border-line/40"> <div className="text-xs text-vermillion-dark pt-1 border-t border-line/40">
with {mem.affiliation} with {mem.affiliation}
</div> </div>
)} )}
@@ -135,13 +135,13 @@ export default function MembersPage() {
<span className="font-serif text-lg font-normal text-ink"> <span className="font-serif text-lg font-normal text-ink">
{mem.ko} {mem.ko}
</span> </span>
<span className="font-mono text-xs text-cobalt-dark dark:text-cobalt"> <span className="font-mono text-xs text-cobalt-dark">
{mem.degree} {mem.degree}
</span> </span>
</div> </div>
<div className="font-mono text-xs text-ink-mute">{mem.en}</div> <div className="font-mono text-xs text-ink-mute">{mem.en}</div>
{mem.affiliation && ( {mem.affiliation && (
<div className="text-xs text-vermillion pt-1 border-t border-line/40"> <div className="text-xs text-vermillion-dark pt-1 border-t border-line/40">
with {mem.affiliation} with {mem.affiliation}
</div> </div>
)} )}
@@ -174,7 +174,7 @@ export default function MembersPage() {
2007년부터 2026년까지 수여된 박사(Ph.D.) 및 석사(M.S.) 졸업생 이력 2007년부터 2026년까지 수여된 박사(Ph.D.) 및 석사(M.S.) 졸업생 이력
</p> </p>
</div> </div>
<span className="font-mono text-xs text-vermillion font-bold border border-vermillion px-3 py-1.5 rounded group-open:bg-vermillion group-open:text-white dark:group-open:text-ivory transition-colors"> <span className="font-mono text-xs text-vermillion-dark font-bold border border-vermillion-dark px-3 py-1.5 rounded group-open:bg-vermillion-dark group-open:text-white dark:group-open:text-ivory transition-colors">
<span className="group-open:hidden">전체 보기 ▾</span> <span className="group-open:hidden">전체 보기 ▾</span>
<span className="hidden group-open:inline">접기 ▴</span> <span className="hidden group-open:inline">접기 ▴</span>
</span> </span>
@@ -199,7 +199,7 @@ export default function MembersPage() {
{alum.major && <span>{alum.major}</span>} {alum.major && <span>{alum.major}</span>}
</div> </div>
{alum.currentPosition && ( {alum.currentPosition && (
<div className="text-xs text-cobalt-dark dark:text-cobalt pt-1 border-t border-line/40"> <div className="text-xs text-cobalt-dark pt-1 border-t border-line/40">
{alum.currentPosition} {alum.currentPosition}
</div> </div>
)} )}
+2 -2
View File
@@ -5,7 +5,7 @@ export default function NotFound() {
return ( return (
<div className="container-content py-20 sm:py-32 flex flex-col items-center justify-center text-center min-h-[60vh]"> <div className="container-content py-20 sm:py-32 flex flex-col items-center justify-center text-center min-h-[60vh]">
<div className="border border-line bg-paper p-8 sm:p-12 rounded-lg max-w-xl w-full space-y-6 shadow-sm"> <div className="border border-line bg-paper p-8 sm:p-12 rounded-lg max-w-xl w-full space-y-6 shadow-sm">
<div className="inline-block px-3 py-1 bg-vermillion/10 text-vermillion font-mono text-xs font-bold rounded tracking-widest uppercase"> <div className="inline-block px-3 py-1 bg-vermillion/10 text-vermillion-dark font-mono text-xs font-bold rounded tracking-widest uppercase">
404 Error 404 Error
</div> </div>
<h1 className="font-serif text-3xl sm:text-4xl font-normal text-ink leading-tight"> <h1 className="font-serif text-3xl sm:text-4xl font-normal text-ink leading-tight">
@@ -19,7 +19,7 @@ export default function NotFound() {
<div className="pt-4"> <div className="pt-4">
<Link <Link
href="/" href="/"
className="inline-flex items-center justify-center px-6 py-3 border border-ink bg-ink text-ivory hover:bg-vermillion hover:border-vermillion transition-colors duration-200 text-sm font-medium rounded-md" className="inline-flex items-center justify-center px-6 py-3 border border-ink bg-ink text-ivory hover:bg-vermillion-dark hover:border-vermillion-dark transition-colors duration-200 text-sm font-medium rounded-md"
> >
메인 페이지로 돌아가기 메인 페이지로 돌아가기
</Link> </Link>
+7 -7
View File
@@ -63,7 +63,7 @@ export default function HomePage() {
{/* Hero Section */} {/* Hero Section */}
<section className="relative"> <section className="relative">
<div className="mb-4"> <div className="mb-4">
<span className="font-mono text-xs tracking-widest uppercase text-cobalt-dark dark:text-cobalt font-bold"> <span className="font-mono text-xs tracking-widest uppercase text-cobalt-dark font-bold">
Kyungpook National University · AI Agent Networking Lab (ANL) Kyungpook National University · AI Agent Networking Lab (ANL)
</span> </span>
</div> </div>
@@ -72,7 +72,7 @@ export default function HomePage() {
<div className="lg:col-span-7 space-y-6 lg:pt-4"> <div className="lg:col-span-7 space-y-6 lg:pt-4">
<h1 className="font-serif text-4xl sm:text-5xl lg:text-6xl font-normal text-ink leading-tight"> <h1 className="font-serif text-4xl sm:text-5xl lg:text-6xl font-normal text-ink leading-tight">
Multi-Agent <br /> Multi-Agent <br />
<span className="italic font-serif text-cobalt-dark dark:text-cobalt">Orchestration</span> <span className="italic font-serif text-cobalt-dark">Orchestration</span>
</h1> </h1>
<p className="text-ink-mute text-base sm:text-lg leading-relaxed max-w-2xl"> <p className="text-ink-mute text-base sm:text-lg leading-relaxed max-w-2xl">
AI 에이전트 네트워크 연구실(ANL)은 멀티 AI 에이전트 오케스트레이션을 위해 서로 소통하고 협력하는 에이전트 네트워킹 기술과 표준 프레임워크 기술을 연구합니다. AI 에이전트 네트워크 연구실(ANL)은 멀티 AI 에이전트 오케스트레이션을 위해 서로 소통하고 협력하는 에이전트 네트워킹 기술과 표준 프레임워크 기술을 연구합니다.
@@ -81,7 +81,7 @@ export default function HomePage() {
{/* Metrics Row (Derived from data array, AC-13) */} {/* Metrics Row (Derived from data array, AC-13) */}
<div className="pt-6 border-t border-line grid grid-cols-3 gap-4"> <div className="pt-6 border-t border-line grid grid-cols-3 gap-4">
<div> <div>
<div className="font-serif text-3xl text-cobalt-dark dark:text-cobalt font-normal"> <div className="font-serif text-3xl text-cobalt-dark font-normal">
{PILLARS.length} {PILLARS.length}
</div> </div>
<div className="font-mono text-xs text-ink-mute uppercase tracking-wider mt-1"> <div className="font-mono text-xs text-ink-mute uppercase tracking-wider mt-1">
@@ -89,7 +89,7 @@ export default function HomePage() {
</div> </div>
</div> </div>
<div> <div>
<div className="font-serif text-3xl text-cobalt-dark dark:text-cobalt font-normal"> <div className="font-serif text-3xl text-cobalt-dark font-normal">
{Math.floor((publications.length + patents.length) / 10) * 10}+ {Math.floor((publications.length + patents.length) / 10) * 10}+
</div> </div>
<div className="font-mono text-xs text-ink-mute uppercase tracking-wider mt-1"> <div className="font-mono text-xs text-ink-mute uppercase tracking-wider mt-1">
@@ -120,7 +120,7 @@ export default function HomePage() {
<section className="space-y-8 pt-20 md:pt-28 border-t border-line"> <section className="space-y-8 pt-20 md:pt-28 border-t border-line">
<div className="flex justify-between items-end"> <div className="flex justify-between items-end">
<div> <div>
<div className="font-mono text-xs text-cobalt-dark dark:text-cobalt uppercase tracking-widest font-bold"> <div className="font-mono text-xs text-cobalt-dark uppercase tracking-widest font-bold">
Research Areas Research Areas
</div> </div>
<h2 className="font-serif text-3xl font-normal text-ink mt-1"> <h2 className="font-serif text-3xl font-normal text-ink mt-1">
@@ -145,7 +145,7 @@ export default function HomePage() {
<div className="flex justify-between items-center"> <div className="flex justify-between items-center">
<span <span
className={`font-mono text-sm font-bold ${ className={`font-mono text-sm font-bold ${
isVermillion ? "text-vermillion" : "text-cobalt-dark dark:text-cobalt" isVermillion ? "text-vermillion" : "text-cobalt-dark"
}`} }`}
> >
[{pillar.no}] [{pillar.no}]
@@ -184,7 +184,7 @@ export default function HomePage() {
<span>ANL Core Spec</span> <span>ANL Core Spec</span>
<span <span
className={ className={
isVermillion ? "text-vermillion" : "text-cobalt-dark dark:text-cobalt" isVermillion ? "text-vermillion" : "text-cobalt-dark"
} }
> >
Pillar #{pillar.no} Pillar #{pillar.no}
@@ -59,8 +59,8 @@ export default function PublicationCategoryPage({ params }: PageProps) {
{/* Header */} {/* Header */}
<section className="space-y-3"> <section className="space-y-3">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<span className="h-px w-8 bg-cobalt-dark dark:bg-cobalt"></span> <span className="h-px w-8 bg-cobalt-dark"></span>
<span className="font-mono text-xs tracking-widest uppercase text-cobalt-dark dark:text-cobalt font-bold"> <span className="font-mono text-xs tracking-widest uppercase text-cobalt-dark font-bold">
Publications Category Publications Category
</span> </span>
</div> </div>
@@ -81,7 +81,7 @@ export default function PublicationCategoryPage({ params }: PageProps) {
<span className="font-mono text-xs text-ink-mute uppercase"> <span className="font-mono text-xs text-ink-mute uppercase">
Showing {records.length} Pre-rendered Records Showing {records.length} Pre-rendered Records
</span> </span>
<span className="font-mono text-xs text-vermillion font-bold"> <span className="font-mono text-xs text-vermillion-dark font-bold">
SSG ● Static HTML SSG ● Static HTML
</span> </span>
</div> </div>
@@ -108,7 +108,7 @@ export default function PublicationCategoryPage({ params }: PageProps) {
</h3> </h3>
<time <time
dateTime={rec.publishedAt} dateTime={rec.publishedAt}
className="font-mono text-xs text-vermillion bg-ivory border border-line px-2.5 py-1 rounded shrink-0 font-bold" className="font-mono text-xs text-vermillion-dark bg-ivory border border-line px-2.5 py-1 rounded shrink-0 font-bold"
> >
{rec.publishedAt} {rec.publishedAt}
</time> </time>
@@ -121,7 +121,7 @@ export default function PublicationCategoryPage({ params }: PageProps) {
출원번호: {rec.appNo} ({rec.country}, {rec.appDate}) 출원번호: {rec.appNo} ({rec.country}, {rec.appDate})
</p> </p>
{rec.regNo && ( {rec.regNo && (
<p className="text-cobalt-dark dark:text-cobalt font-bold"> <p className="text-cobalt-dark font-bold">
등록번호: {rec.regNo} ({rec.regDate}) 등록번호: {rec.regNo} ({rec.regDate})
</p> </p>
)} )}
@@ -20,7 +20,7 @@ export function CategoryNav({ currentCategory }: CategoryNavProps) {
className={`px-4 py-2 rounded text-xs font-mono tracking-wider transition-colors ${ className={`px-4 py-2 rounded text-xs font-mono tracking-wider transition-colors ${
isActive isActive
? "bg-cobalt-dark text-white dark:text-ivory font-bold" ? "bg-cobalt-dark text-white dark:text-ivory font-bold"
: "bg-paper text-ink border border-line hover:border-cobalt-dark dark:hover:border-cobalt" : "bg-paper text-ink border border-line hover:border-cobalt-dark"
}`} }`}
> >
{cat.label} {cat.label}
+5 -5
View File
@@ -26,8 +26,8 @@ export default function PublicationsIndexPage() {
{/* Header section */} {/* Header section */}
<section className="space-y-4"> <section className="space-y-4">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<span className="h-px w-8 bg-cobalt-dark dark:bg-cobalt" /> <span className="h-px w-8 bg-cobalt-dark" />
<span className="font-mono text-xs font-bold uppercase tracking-widest text-cobalt-dark dark:text-cobalt"> <span className="font-mono text-xs font-bold uppercase tracking-widest text-cobalt-dark">
BIBLIOGRAPHY &amp; INTELLECTUAL PROPERTY BIBLIOGRAPHY &amp; INTELLECTUAL PROPERTY
</span> </span>
</div> </div>
@@ -49,13 +49,13 @@ export default function PublicationsIndexPage() {
return ( return (
<div <div
key={cat.slug} key={cat.slug}
className="flex flex-col justify-between border border-line bg-paper p-6 rounded-lg transition-colors hover:border-cobalt-dark dark:hover:border-cobalt group" className="flex flex-col justify-between border border-line bg-paper p-6 rounded-lg transition-colors hover:border-cobalt-dark group"
> >
<div className="space-y-3"> <div className="space-y-3">
<span className="font-mono text-xs text-ink-mute uppercase tracking-wider"> <span className="font-mono text-xs text-ink-mute uppercase tracking-wider">
Category Category
</span> </span>
<h2 className="headline-ko text-xl text-ink font-bold group-hover:text-cobalt-dark dark:group-hover:text-cobalt transition-colors"> <h2 className="headline-ko text-xl text-ink font-bold group-hover:text-cobalt-dark transition-colors">
{cat.label} {cat.label}
</h2> </h2>
<p className="text-xs text-ink-mute leading-relaxed"> <p className="text-xs text-ink-mute leading-relaxed">
@@ -69,7 +69,7 @@ export default function PublicationsIndexPage() {
</span> </span>
<Link <Link
href={`/publications/${cat.slug}`} href={`/publications/${cat.slug}`}
className="inline-flex items-center text-xs font-bold text-vermillion hover:underline" className="inline-flex items-center text-xs font-bold text-vermillion-dark hover:underline"
> >
전체 보기 &rarr; 전체 보기 &rarr;
</Link> </Link>
@@ -13,8 +13,8 @@ export default function StandardizationPage() {
{/* Header */} {/* Header */}
<section className="space-y-3"> <section className="space-y-3">
<div className="flex items-center gap-3"> <div className="flex items-center gap-3">
<span className="h-px w-8 bg-cobalt-dark dark:bg-cobalt"></span> <span className="h-px w-8 bg-cobalt-dark"></span>
<span className="font-mono text-xs tracking-widest uppercase text-cobalt-dark dark:text-cobalt font-bold"> <span className="font-mono text-xs tracking-widest uppercase text-cobalt-dark font-bold">
Open Standards &amp; Contributions Open Standards &amp; Contributions
</span> </span>
</div> </div>
@@ -33,7 +33,7 @@ export default function StandardizationPage() {
<div className="flex flex-wrap justify-between items-start gap-4 border-b border-line pb-4"> <div className="flex flex-wrap justify-between items-start gap-4 border-b border-line pb-4">
<div> <div>
<div className="flex items-center gap-2"> <div className="flex items-center gap-2">
<span className="font-mono text-xs text-cobalt-dark dark:text-cobalt font-bold uppercase"> <span className="font-mono text-xs text-cobalt-dark font-bold uppercase">
[{body.scope}] [{body.scope}]
</span> </span>
{body.role && ( {body.role && (
@@ -61,7 +61,7 @@ export default function StandardizationPage() {
<div key={pIdx} className="bg-ivory p-6 rounded border border-line space-y-3"> <div key={pIdx} className="bg-ivory p-6 rounded border border-line space-y-3">
<div className="font-serif text-lg font-normal text-ink border-b border-line/60 pb-2 flex justify-between items-center"> <div className="font-serif text-lg font-normal text-ink border-b border-line/60 pb-2 flex justify-between items-center">
<div> <div>
<span className="font-mono text-xs text-cobalt-dark dark:text-cobalt font-bold mr-2"> <span className="font-mono text-xs text-cobalt-dark font-bold mr-2">
{proj.wg} {proj.wg}
</span> </span>
<span>{proj.name}</span> <span>{proj.name}</span>
+2 -2
View File
@@ -50,7 +50,7 @@ export default function Footer() {
경북대학교 IT대학 5호관(415동) 527호<br /> 경북대학교 IT대학 5호관(415동) 527호<br />
<a <a
href="mailto:sjkoh@knu.ac.kr" href="mailto:sjkoh@knu.ac.kr"
className="draw-underline mt-2 inline-block text-ink hover:text-vermillion font-medium" className="draw-underline mt-2 inline-block text-ink hover:text-vermillion-dark font-medium"
> >
sjkoh@knu.ac.kr sjkoh@knu.ac.kr
</a> </a>
@@ -68,7 +68,7 @@ export default function Footer() {
</span> </span>
<Link <Link
href={l.href} href={l.href}
className="text-sm text-ink-soft transition hover:text-vermillion" className="text-sm text-ink-soft transition hover:text-vermillion-dark"
> >
{l.ko}{" "} {l.ko}{" "}
<span className="text-xs text-ink-mute">{l.en}</span> <span className="text-xs text-ink-mute">{l.en}</span>
+1 -1
View File
@@ -67,7 +67,7 @@ export default function Header() {
className={`group relative flex items-baseline gap-1.5 text-[0.78rem] font-semibold uppercase tracking-[0.18em] transition-all px-3 py-1.5 rounded-md ${ className={`group relative flex items-baseline gap-1.5 text-[0.78rem] font-semibold uppercase tracking-[0.18em] transition-all px-3 py-1.5 rounded-md ${
active active
? "bg-cobalt-dark text-white dark:text-ivory shadow-sm font-bold" ? "bg-cobalt-dark text-white dark:text-ivory shadow-sm font-bold"
: "text-ink hover:text-cobalt-dark dark:hover:text-cobalt hover:bg-paper/60" : "text-ink hover:text-cobalt-dark hover:bg-paper/60"
}`} }`}
> >
<span <span
+12 -5
View File
@@ -325,9 +325,11 @@ def test_color_contrast_gate():
# AC-44: Verify text-cobalt or text-vermillion (DEFAULT) is not combined with small text sizes # AC-44: Verify text-cobalt or text-vermillion (DEFAULT) is not combined with small text sizes
# AC-47: Verify text-white utility usage <= 8 instances # AC-47: Verify text-white utility usage <= 8 instances
# C11-9 (AC-48): Verify zero dark: modifier usage on accent colors (R-8.29)
white_count = 0 white_count = 0
small_size_pattern = re.compile(r'text-(xs|sm|\[0\.65rem\]|\[0\.78rem\])') small_size_pattern = re.compile(r'text-(xs|sm|\[0\.65rem\]|\[0\.78rem\])')
default_color_pattern = re.compile(r'(?<!dark:)text-(cobalt|vermillion)(?![a-z-])') default_color_pattern = re.compile(r'text-(cobalt|vermillion)(?![a-z-])')
dark_accent_pattern = re.compile(r'dark:(?:[a-z:-]*)(cobalt|vermillion)')
for root_dir in [os.path.join(REFER_DIR, "app"), os.path.join(REFER_DIR, "components")]: for root_dir in [os.path.join(REFER_DIR, "app"), os.path.join(REFER_DIR, "components")]:
for root, _, files in os.walk(root_dir): for root, _, files in os.walk(root_dir):
@@ -340,11 +342,16 @@ def test_color_contrast_gate():
# AC-47 count # AC-47 count
white_count += content.count("text-white") white_count += content.count("text-white")
# AC-44 check line by line # AC-44 & C11-9 check line by line
for line_idx, line in enumerate(content.splitlines(), start=1): for line_idx, line in enumerate(content.splitlines(), start=1):
# C11-9: Zero dark: modifier on accent colors (allow dark:text-ivory / dark:group-open:text-ivory exceptions)
if dark_accent_pattern.search(line):
print(f"❌ C11-9 (AC-48) Violation: Forbidden dark: modifier on accent color at {fpath}:{line_idx}\n {line.strip()}")
return False
if small_size_pattern.search(line) and default_color_pattern.search(line): if small_size_pattern.search(line) and default_color_pattern.search(line):
# Ignore allowed exceptions (e.g. HeroComposition, Pillar bullets, Vermillion kickers or large titles) # Ignore allowed exceptions (e.g. HeroComposition, Pillar bullets, or explicitly exempted titles)
if "Pillar #" in line or "w-1.5 h-1.5" in line or "text-3xl" in line or "text-vermillion" in line: if "Pillar #" in line or "w-1.5 h-1.5" in line or "text-3xl" in line:
continue continue
print(f"❌ AC-44 Violation: Small text combined with DEFAULT accent color at {fpath}:{line_idx}\n {line.strip()}") print(f"❌ AC-44 Violation: Small text combined with DEFAULT accent color at {fpath}:{line_idx}\n {line.strip()}")
return False return False
@@ -353,7 +360,7 @@ def test_color_contrast_gate():
print(f"❌ AC-47 Violation: text-white usage count ({white_count}) exceeds whitelist limit (8)") print(f"❌ AC-47 Violation: text-white usage count ({white_count}) exceeds whitelist limit (8)")
return False return False
print(f"✅ Gate 10 PASSED: Color contrast (AC-43), small text routing (AC-44), inversion (AC-45), and text-white count ({white_count}/8, AC-47) verified") print(f"✅ Gate 10 & 11 PASSED: Color contrast (AC-43), small text routing (AC-44), inversion (AC-45), text-white count ({white_count}/8, AC-47), and dark: accent modifier zero check (C11-9) verified")
return True return True
def main(): def main():