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>
This commit is contained in:
+319
@@ -0,0 +1,319 @@
|
||||
# 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 외**. 데모 우선.
|
||||
Reference in New Issue
Block a user