Files
KyeongminandClaude Opus 4.8 62df988114 docs(#9): emergency.md §7 체크리스트 실측 갱신 + PHASE_Z_ALLOW_* dead env retire
- emergency.md: P2/P2.5/P3/P5 = 구현·작동 실측 확인 표기, P4 = envelope 수정(9717898) 기록
- vite.config.ts: PHASE_Z_ALLOW_REJECT / PHASE_Z_ALLOW_RESTRUCTURE retire
  (src read 0곳 — Emergency P2 가 reject 보존을 기본 정책화하여 불필요, §4 Quick fix 결정 B)
- 검증: tsc --noEmit 0 에러

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

16 KiB

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-202-2.1/02-2.2, 04-204-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-904PHASE_Z_ALLOW_REJECT: "1" / PHASE_Z_ALLOW_RESTRUCTURE: "1"
  • src/ — 현재 read 0 매치

결정 안:

  • A. Wire-up: lookup_v4_candidatesinclude_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.tsPHASE_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.

  • 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) — 2026-07-02 실측 검증: mdx04 04-1 reject 후보 ai_adaptation_required 로 보존 (GitHub issue #9)
  • P2.5: Section key alignment policy (C2 처리 — child → parent pool merge, universal) — 2026-07-02 실측: 02-2 가 parent merge 로 frame 14 획득
  • P3: empty_shell terminal 제거 + AI adaptation + generic_fallback frame mandatory (C1/C3 처리) — 2026-07-02 실측: mdx05 GENERIC_FALLBACK 렌더, 5/5 mdx empty shell 0
  • P4: AI fallback 호출 trace — 2026-07-02: 호출은 됐으나 proposal envelope 불일치로 전량 ValidationError 유실 발견 → 수정 (커밋 9717898: 프롬프트 envelope 명시 + client 코어싱). 잔여: design_adaptation_plan apply 계층 미구현 (unsupported_kind — issue #7 axis)
  • P5: Frontend candidate panel 검증 (status surface) — FramePanel candidate_status 툴팁 + route 배지 구현 확인
  • Quick fix: PHASE_Z_ALLOW_REJECT wire-up 또는 retire (미확인 — dead env var 여부 재점검 필요)

8. 후속 정리 (이 세션 후)

작업 안정화 되면 별도 task 로:

  • 새 Gitea issue 생성 (P1~P5 결과 evidence 기반)
  • 분리된 commits 으로 push
  • 23 suspect pool deep-dive (별 axis)
  • queue tracker #97 update

이 단계는 이번 세션 scope 외. 데모 우선.