Files
C.E.L_Slide_test2/emergency.md
T
KyeongminandClaude Opus 4.8 b836e79ee1 wip: phase_z2 evidence 파이프라인 + matching 실험(phase2~26) + 프론트 trace 패널 진행분 스냅샷
- src: phase_z2 composition/mapper/pipeline/placement_planner/retry, ai_fallback(prompts/schema/validate), mdx_text_atoms 신규
- Front: PipelineTracePanel 신규, FramePanel/SlideCanvas/Home/designAgentApi 등 갱신 + 테스트 4종 추가
- templates/phase_z2: catalog(component_expansion_registry, node_slot_mapping 신규), frames, families, slide_base 갱신
- tests/matching: phase2~26 매칭 실험 스크립트·리포트·온톨로지 전체 (미커밋 진행분)
- tests: b4_v4 evidence, task5~28.5 시리즈, regression(imp95 baseline) 등 신규 테스트 대량 추가
- docs/reference: MDX 구조 인벤토리, MDX→Frame 구조 계약 문서
- scripts: mdx 계약/parity/coverage/viewport 체크, gitea comment, run sync 유틸
- .gitignore: tmp*.json, chromedriver, .orchestrator, *.pkl, Front_test* 등 임시/스냅샷 제외

미완성 작업의 보존용 스냅샷 커밋 (2026-07-02)

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 17:03:42 +09:00

320 lines
15 KiB
Markdown

# Emergency Session — mdx 01~05 visible-result rescue
**시작**: 2026-05-26
**목표 시간**: ~1 시간
**모드**: 로컬 코드 개선 (no Gitea push, no orchestrator, no issue 생성)
---
## 1. 목표
프론트에서 mdx 01~05 **5 개 모두 반드시 슬라이드 출력** (empty shell 금지) + 사용자가 frame 선택/변경 가능 + 선택된 frame 으로 AI 가 원문 보존하며 재배치.
### 정책 lock (한 줄)
> **empty shell 금지. 어떤 경우든 원문 텍스트 보존 + 선택 가능 frame 또는 fallback frame 에 AI 가 재배치.**
**현재 상태**:
| mdx | 결과 |
|---|---|
| 03 | ✅ direct path — 정상 |
| 04 | ❌ 04-1 reject → provisional, 04-2 plan 에서 누락. 1 slide 만 |
| 05 | ❌ 05-1 + 05-2 empty_shell. 빈 placeholder |
| 01, 02 | unknown (이번 세션 sanity check 필요) |
**기대 (정책 적용 후)**: 모든 mdx 의 모든 section 이 슬라이드로 렌더 — reject-only 여도 AI 재배치 path 로 surface.
---
## 2. 운영 원칙 (lock)
- ❌ git push 금지
- ❌ Gitea issue 생성 / orchestrator 실행 금지
- ❌ 결과물 하드코딩 금지 (mdx 별 특수 처리 X)
- ❌ MDX 원문 텍스트 변경 금지
- ❌ 후보 수 강제 floor (3~6 padding) 금지
-**empty_shell 종결 금지** — 후보 reject-only 라도 AI adaptation route 필수
-**reject 후보 자동 렌더 금지** — 항상 ai_adaptation_required 로 status 부여
- ✅ 코드 path / 정책 / fallback 수정
- ✅ 각 edit 후 로컬 run 으로 검증
- ✅ Codex + 사용자 + Claude consensus 매 step
---
## 3. Root cause 진단 (확정)
3-layer contradiction:
```
Layer 1: vite.config.ts spawn env
PHASE_Z_ALLOW_REJECT="1" ← 의도: reject 허용
← 실제: src/ 에서 read 안 됨 (dead env var)
Layer 2: src/phase_z2_pipeline.py:1481-1520
lookup_v4_non_reject_candidates()
if j.get("label") == "reject": continue ← reject 강제 drop (May 8 lock)
Layer 3: composition + IMP-30 u4
selection_path = "empty_shell"
imp30_u4 = "terminal_first_render_empty_shell" ← AI fallback 도달 차단
```
**결과**: reject 후보가 있어도 → filter 에서 drop → empty_shell → terminal → AI fallback 미호출.
**Cross-reference**:
- `.env: AI_FALLBACK_ENABLED=true` ✅ 활성
- 사용자 정책 ([feedback_ai_isolation_contract]): "reject = AI 재구성 대상 (frame 유지 + AI 재배치)"
- IMP-47B (#76): reject 후보 surface = override path 만 (자동 path 미포함)
### Resolution — 통합 정책
3-layer contradiction 의 해소 = **§1 의 정책 lock ("empty shell 금지")** 적용 :
1. Layer 2 의 reject filter → **status 분류로 대체** (제거 X, 모두 보존 + status 부여)
2. Layer 3 의 empty_shell terminal → **제거** (어떤 후보 상태든 AI adaptation route 로 흐름)
3. Layer 1 의 dead env → wire-up 또는 retire (§4 Quick fix)
### P1 결과 (2026-05-26) — 3 종류 root cause 동시 존재 (단순 reject filter X)
P1 검증으로 처음 가정 ("주로 reject filter 문제") 와 실제 가 다름 발견 :
| 분류 | 영향 section | 처리 priority |
|---|---|---|
| **C1. All-reject (catalog gap)** | `02-2.1`, `04-1` (raw 32 모두 reject) | **P2** — reject = `ai_adaptation_required` (status 분류) |
| **C2. Section key mismatch** | `02-2``02-2.1/02-2.2`, `04-2``04-2.1/04-2.2` (V4 source 가 split) | **P2.5** — parent ↔ child key alignment policy (universal) |
| **C3. V4 source 자체 누락** | `mdx 05 전체` (entry 0) | **P3** — generic fallback frame mandatory (raw 후보 0 일반 정책) |
**fixture 주의** : `tests/matching/v4_full32_result.yaml` = 2026-04-29 stale snapshot. 이번 세션 = fallback policy 강화로 fixture 의존 약화. runtime V4 evaluator migrate = 별 axis (이번 세션 후).
### Candidate status 3 분류 (정책 기반)
| status | 후보 label | 처리 |
|---|---|---|
| `auto_renderable` | use_as_is / light_edit / restructure (일부) | 즉시 직접 렌더 가능 |
| `ai_adaptation_required` | needs_adaptation / reject / fallback_candidate | 자동 렌더 X, AI 재배치 필요 |
| `blocked_runtime` | visual_pending / catalog scaffolding 등 | 현재 시스템상 렌더 불가 (별 axis 로 추적) |
**규칙**:
- `auto_renderable` 1+ → 그 중 rank-1 으로 직접 렌더
- `auto_renderable` 0 + `ai_adaptation_required` 1+ → top 후보 AI adaptation route
- 둘 다 0 → generic_fallback frame + AI adaptation (별 axis — 이번 세션 후순위, §4 결정 보류 참조)
- **어떤 경우든 empty_shell 종결 X**
---
## 4. 작업 순서 (5 priority + 1 quick fix)
### Priority 1 — Raw 후보 분포 확인 (5~10분, read-only)
**목적**: catalog gap (후보 없음) vs matching gap (후보 있지만 reject) 구분.
**파일**:
- `tests/matching/v4_full32_result.yaml` (raw 32 candidates source)
- `data/runs/mdx04_clean_check_v2/phase_z2/steps/step05_v4_evidence.json`
- `data/runs/mdx05_clean_check_v2/phase_z2/steps/step05_v4_evidence.json`
**검증 질문**:
- 04-1, 04-2, 05-1, 05-2 각각의 raw 32 candidates 중 reject 수 / non-reject 수?
- 어떤 frame_template_id 들이 reject 됐나? (catalog 의 가능성)
- candidate_status="no_non_reject_v4_candidate" 가 정말 모든 4 섹션?
### Priority 2 — V4 후보 보존 + status 분류 (15~20분, edit)
**목적**: reject 후보 삭제 X, 모두 보존 + `candidate_status` 부여 (auto_renderable / ai_adaptation_required / blocked_runtime).
**파일 + 함수**:
- `src/phase_z2_pipeline.py:1481-1520` `lookup_v4_non_reject_candidates`
- rename → `lookup_v4_candidates(v4, section_id, max_n=6, include_reject=True, alias_keys=None)`
- **default = `include_reject=True`** (정책 lock 반영)
- 모든 후보 반환 + 각 V4Match 에 `candidate_status` field 추가
- reject filter 제거 — visual_pending 만 `blocked_runtime` 로 표시 (drop X)
**V4Match dataclass / dict 확장**:
- `candidate_status: str` field 추가
- 분류 규칙 (단일 source of truth):
- label ∈ {use_as_is, light_edit} → `auto_renderable`
- label = restructure → `auto_renderable` (단, capacity_fit 통과 시) / `ai_adaptation_required` (mismatch 시)
- label ∈ {needs_adaptation, reject, fallback_candidate} → `ai_adaptation_required`
- visual_pending = True → `blocked_runtime`
**호출처 검색 필요**:
- `grep -n lookup_v4_non_reject_candidates src/`
- 호출처 마다 status field 사용 여부 확인 + 필요 시 분기 추가
**composition 변경**:
- `src/phase_z2_composition.py` (line ~681 부근)
- 후보 retrieve → status 별 분류:
- `auto_renderable` 1+ → rank-1 으로 직접 렌더
- 그 외 → `ai_adaptation_required` route (P3 의 empty_shell 우회 와 연결)
### Priority 2.5 — Section key alignment policy (10~15분, edit) [NEW — C2 처리]
**목적**: pipeline normalize 의 section_id (예: `04-2`) 와 V4 source 의 split child keys (예: `04-2.1`, `04-2.2`) 매칭. **universal policy** — 02-2 / 04-2 모두 cover, mdx 별 dispatch X.
**접근 — "child → parent pool merge" (Codex 안)** :
parent 를 child 로 split 하는 방식 (composition planner 변경) 보다 빠르고 안전 :
```
section_id = X (e.g., "04-2")
lookup_v4_candidates(X) → V4 에 X 직접 매칭 시도
hit → return as-is
miss → fallback: child keys 검색 (X.1, X.2, X.3, ...)
hit → 모든 child candidates 를 X 의 candidate pool 로 merge
(origin: child_id 보존 — debug)
miss → return [] (P3 의 generic fallback 으로 흐름)
```
**파일**:
- `src/phase_z2_pipeline.py:1505` `_resolve_v4_section_key` — alias 기능 이미 일부 있음 (alias_keys param). 확장 필요.
- `lookup_v4_candidates` (P2 에서 신설) — alias miss 시 child key scan 추가
**검증 sample**:
- `04-2` lookup → V4 miss → `04-2.1` + `04-2.2` 찾음 → 5 candidates pool 로 merge (04-2.1=2, 04-2.2=3)
- `02-2` lookup → V4 miss → `02-2.1` + `02-2.2` 찾음 → 5 candidates pool
### Priority 3 — empty_shell terminal 제거 + AI adaptation + generic fallback mandatory (15~20분, edit)
**목적**: `terminal_first_render_empty_shell` (IMP-30 u4) **제거** — 어떤 후보 상태든 AI adaptation 으로 분기. **empty_shell 종결 = 0 보장**. mdx 05 같은 raw 후보 0 케이스도 cover (C3).
**파일**:
- `src/phase_z2_composition.py` 또는 `src/phase_z2_pipeline.py` 의 IMP-30 u4 분기점
- 검색: `terminal_first_render_empty_shell`, `empty_shell`, `__empty__` literal
**정책 결정 (lock — universal, mdx 별 X)**:
| condition | route |
|---|---|
| `auto_renderable` 1+ | rank-1 으로 직접 렌더 |
| `auto_renderable` 0 + `ai_adaptation_required` 1+ | top 후보 AI adaptation route. selection_path = `"ai_adaptation_required"`, frame_template_id = 실제 frame |
| `auto_renderable` 0 + `ai_adaptation_required` 0 (raw 후보 0 — C3) | **generic_fallback frame + AI adaptation** (mandatory, no longer deferred) |
**generic_fallback frame 정책**:
- catalog 의 가장 universal frame 1 개 지정 (예: simple bullet + title 형)
- **신설 frame X — 기존 catalog 활용**
- hardcode mdx 별 dispatch X — "raw 후보 0 일 때" 통일 트리거
- selection_path = `"generic_fallback"`, candidate_status = `"ai_adaptation_required"` (AI 가 원문 재배치)
- catalog_gap metric 기록 (운영 시그널 — terminal X)
**무엇이 더 이상 존재하지 않는가**:
- `merge_type: "empty_shell"`
- `selection_path: "empty_shell"`
- `frame_template_id: "__empty__"`
- `imp30_u4: "terminal_first_render_empty_shell"` (이게 terminal 이라는 의미)
**기록은 유지** (운영 시그널):
- `candidate_status_summary` 같은 metric 으로 catalog_gap 비율 추적 — 단 terminal 아님
### Priority 4 — AI fallback 호출 trace (5~10분, read + verify)
**목적**: .env 가 `AI_FALLBACK_ENABLED=true` 인데 실제 호출되는지 확인. empty_shell 전에 끊기는지.
**파일**:
- `src/phase_z2_ai_fallback/router.py:67` (`if not settings.ai_fallback_enabled:` gate)
- `src/phase_z2_pipeline.py:4914` (`if not _settings.ai_fallback_enabled:`)
**검증**:
- mdx 04/05 로컬 run 후 `step12_ai_repair.json` 확인 — AI 호출 attempt 있나?
- 없으면 Priority 3 fix 후 다시 확인
### Priority 5 — Frontend candidate panel 검증 (5~10분, read + edit)
**목적**: backend 후보 살아도 frontend 숨기면 사용자 못 씀. reject / restructure / needs_adaptation 후보가 우측 panel 에 나와야.
**파일**:
- `Front/client/src/components/FramePanel.tsx` (candidate rendering 로직)
- `Front/client/src/components/SlideCanvas.tsx` (candidate selection 흐름)
- API: `/api/run` response 의 candidate 필드 확인
**검증**:
- mdx 04/05 로컬 run 결과 → frontend 가 reject 후보를 표시 / 선택 가능한지
### Quick fix (5분) — PHASE_Z_ALLOW_REJECT wire-up 또는 retire
**파일**:
- `Front/vite.config.ts:903-904``PHASE_Z_ALLOW_REJECT: "1"` / `PHASE_Z_ALLOW_RESTRUCTURE: "1"`
- `src/` — 현재 read 0 매치
**결정 안**:
- **A. Wire-up**: `lookup_v4_candidates``include_reject` 가 이 env 도 source 로 read
- **B. Retire**: vite spawn env 에서 제거 (dead var 제거)
선택은 작업 진행 중 결정.
---
## 5. 검증 — Acceptance criteria
### 각 priority 완료 후 sanity
| priority | local run | 기대 |
|---|---|---|
| 1 | (read-only) | ✅ done — 04/05 raw 후보 분포 + C1/C2/C3 root cause 식별 |
| 2 | mdx04/05 local pipeline | step05_v4_evidence 의 각 candidate 에 `candidate_status` field 존재 (auto / ai_adaptation / blocked), reject 후보 surface — **C1 처리** |
| 2.5 | mdx02/04 local pipeline | `04-2` lookup → child `04-2.1` + `04-2.2` 후보 5 개 merge 됨 (debug log 에 origin child_id 보존), `02-2` 도 동일 — **C2 처리** |
| 3 | mdx04/05 local pipeline | step06_composition_plan 의 `selection_path` ∈ {`auto_renderable_rank_1`, `ai_adaptation_required`, `generic_fallback`}, **`"empty_shell"` 0 회**, `frame_template_id != "__empty__"`, mdx 05 도 generic_fallback 으로 슬라이드 — **C1/C3 처리** |
| 4 | mdx04/05 local pipeline | step12_ai_repair.json 에 AI 호출 trace 존재 (ai_adaptation route 도달) |
| 5 | frontend 새로고침 | mdx 04/05 의 우측 panel 에 후보 surface + status 별 시각 구분 (예: 배지) |
### 최종 demo readiness
- [ ] mdx 01: 의미 있는 슬라이드 나옴
- [ ] mdx 02: 의미 있는 슬라이드 나옴 (02-2 = C2 alignment 검증)
- [ ] mdx 03: 정상 (회귀 X 검증)
- [ ] mdx 04: 04-1 (C1 reject→AI), 04-2 (C2 alignment) 모두 슬라이드 나옴
- [ ] mdx 05: 05-1 + 05-2 generic_fallback 으로 슬라이드 나옴 (C3 처리)
- [ ] **5 mdx 통합: empty_shell 결과 = 0** (어떤 mdx 도 empty 안 됨 — 정책 lock 검증)
- [ ] **C2 검증**: 04-2 가 04-2.1/04-2.2 child candidates 받아옴 (parent ↔ child alignment universal)
- [ ] **C3 검증**: mdx 05 가 V4 source 없음에도 generic_fallback frame + AI adaptation 으로 슬라이드 (raw 후보 0 일반 정책)
- [ ] 각 슬라이드 frame 변경 가능 (panel UI, status 분류 surface)
- [ ] AI 적용/재배치 동작 (mdx 텍스트 보존)
- [ ] reject-only mdx (mdx 04-1, mdx 02-2.1) 가 AI adaptation 으로 슬라이드 생성 — 정책 lock end-to-end 검증
---
## 6. 수정 대상 파일 — 최종 inventory
**Edit 대상**:
- `src/phase_z2_pipeline.py` — lookup_v4 함수 + Step 5/9 caller + empty_shell 분기
- `src/phase_z2_composition.py` — empty_shell terminal disposition 우회 + reject 후보 routing
- (조건부) `src/phase_z2_ai_fallback/router.py` — gate 확인 후 필요 시
- (조건부) `Front/vite.config.ts``PHASE_Z_ALLOW_REJECT` wire-up 또는 retire
- (조건부) `Front/client/src/components/FramePanel.tsx` — reject 후보 surface 검증/추가
**Read-only 진단**:
- `tests/matching/v4_full32_result.yaml`
- `data/runs/mdx04_clean_check_v2/phase_z2/steps/step05_v4_evidence.json`
- `data/runs/mdx04_clean_check_v2/phase_z2/steps/step06_composition_plan.json`
- `data/runs/mdx05_clean_check_v2/phase_z2/steps/step05_v4_evidence.json`
- `data/runs/mdx05_clean_check_v2/phase_z2/steps/step06_composition_plan.json`
- `data/runs/mdx05_clean_check_v2/phase_z2/steps/step12_ai_repair.json`
- (priority 4 진행 후) 새 run 의 동일 step files
**MDX 원문 (절대 변경 X)**:
- `samples/mdx_batch/01.mdx` ~ `05.mdx`
---
## 7. 진행 추적
각 priority 완료 후 이 섹션에 결과 한 줄 추가 — 작업 끝나면 정리해서 후속 Gitea issue / commit 본문 reference.
- [x] P1: Raw 후보 분포 확인 — C1 (02-2.1, 04-1 all-reject) / C2 (04-2 ↔ 04-2.1/04-2.2 split) / C3 (mdx 05 source 누락) 식별
- [ ] P2: V4 후보 보존 + 3-status 분류 (C1 처리 — reject = ai_adaptation_required)
- [ ] P2.5: Section key alignment policy (C2 처리 — child → parent pool merge, universal)
- [ ] P3: empty_shell terminal 제거 + AI adaptation + generic_fallback frame mandatory (C1/C3 처리)
- [ ] P4: AI fallback 호출 trace
- [ ] P5: Frontend candidate panel 검증 (status surface)
- [ ] Quick fix: PHASE_Z_ALLOW_REJECT wire-up 또는 retire
---
## 8. 후속 정리 (이 세션 후)
작업 안정화 되면 별도 task 로:
- 새 Gitea issue 생성 (P1~P5 결과 evidence 기반)
- 분리된 commits 으로 push
- 23 suspect pool deep-dive (별 axis)
- queue tracker #97 update
이 단계는 **이번 세션 scope 외**. 데모 우선.