Files
C.E.L_Slide_test2/.orchestrator/tmp/codex_prompt.txt
T

664 lines
43 KiB
Plaintext
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
ISSUE #76: IMP-47B reject-as-AI-adaptation activation (reject route AI 재구성 활성화)
URL: https://gitea.hmac.kr/Kyeongmin/C.E.L_Slide_test2/issues/76
=== ISSUE BODY ===
**관련 step**: Step 12 reject route — reject = AI 재구성 대상 정책 활성화
**source**: 사용자 정책 정정 (2026-05-21) — #61 의 design_reference_only_no_ai 정책 정정
**roadmap axis**: R3 (AI 보정/재구성) — destination 본체
**wave**: 1 (실질 구동 도달 필수)
**priority**: ★ destination 시연 path 본체
**dependency**: **IMP-47A** verified (frontend 안정화 선행), **#62 IMP-46** verified (cache 결정론 보장), #61 IMP-33 verified (AI scaffold), #39 IMP-30 verified (first-render invariant — provisional zone)
**pair**: IMP-47A (frontend 안정화 선행)
**최종 정책 (사용자 명시 2026-05-21)**:
1. `use_as_is` / `light_edit` → 코드 기반 자동 렌더
2. `restructure` → AI 재구성 대상
3. **`reject` (all reject 또는 사용자가 reject frame 선택) → rank 1 reject frame 유지 + AI 재구성**
4. 절대 금지: 다른 rank frame 으로 자동 교체 (frame swap) / MDX 내용 삭제 / frame visual 임의 변경
**scope** (7 작업):
1. **reject route 정정** — `design_reference_only_no_ai` → `ai_adaptation_required` 계열
- `src/phase_z2_pipeline.py` 의 `APPLICATION_MODE_BY_V4_LABEL` 또는 `_IMP05_ROUTE_HINTS` 정정
- `reject` label 도 AI fallback hook 대상으로 routing
2. **all reject 시 rank 1 reject 를 provisional unit 으로 살림**
- 현재: V4 가 어떤 section 에도 rank 1 매칭 못함 → no_v4_candidate → __empty__ provisional shell
- 정정: 모든 후보가 reject 라도 rank 1 reject 를 provisional unit 으로 채택 (IMP-30 first-render invariant 와 연결)
3. **사용자 override reject 도 AI 대상**
- frontend FramePanel 에서 사용자가 reject 카드 명시 클릭 → backend `--override-frame` 전달 → 해당 reject frame 유지 + AI 재구성
4. **AI proposal 생성 → slot_payload 적용**
- 입력: 선택된 reject frame 의 visual / contract / cardinality + MDX 원문
- 출력: builder_options / partial_overrides / slot_mapping (MDX 내용을 frame slot 에 100% 보존하면서 배치)
- 이미 #61 scaffold 의 `phase_z2_ai_fallback` 에 hook 존재 — flag ON + reject route 추가만
5. **final.html 재렌더**
- AI proposal 적용 후 Step 13 render 재실행
- frontend iframe 자동 재로드
6. **content 100% coverage 검증**
- Step 20 slide_status 의 `covered` set 이 모든 section 포함하는지 검증
- text_block / table / image / details 삭제 X (dropped 절대 룰)
7. **실패 시 human_review / user override 명확 표시**
- AI 호출 실패 / proposal validation 실패 / coverage 미달 → frontend 에 명확한 notification ("이 frame 으로 AI 재구성 실패. 다른 frame 선택 또는 manual 편집 필요")
**out of scope** (별 IMP):
- frontend stabilization → **IMP-47A** (선행)
- cache 자체 → **#62 IMP-46** (선행)
- HTML 직접 편집 → R6 (추후)
- overflow chain (zone resize / popup / responsive fit) → IMP-34 / 35 / 36
- mdx04 / 05 의 raw HTML 처리 → IMP-37 (stage0 normalizer)
**guardrail / validation**:
- ★ **AI 호출 = fallback path only**. normal path (use_as_is / light_edit) AI 호출 = 0 (memory `feedback_ai_isolation_contract`)
- ★ **MDX 원문 100% 보존**. AI proposal 출력은 slot mapping / builder_options / partial_overrides 만 — MDX text 변경 X
- ★ **자동 frame swap 금지**. V4 rank 1 reject frame 유지 (다른 rank frame 으로 교체 X)
- ★ **frame visual 임의 변경 금지**. frame partial / contract 무변 — slot 채우기 / cardinality 배치만
- ★ **dropped 절대 룰**: text_block / table / image / details 삭제 X
- ★ AI fallback ON + route gate + per-run budget + circuit breaker + cache + validation 모두 적용 (runaway 방지)
- no-hardcoding: reject 처리 path 가 sample-specific X
- mdx03 reject 라벨 frame 으로 end-to-end 검증: 사용자 클릭 → AI 호출 → cache hit/miss → proposal apply → final.html → iframe 표시
**cross-ref**:
- source: 사용자 정책 정정 (2026-05-21)
- depend: IMP-47A (선행 — frontend 안정화), #62 IMP-46 (선행 — cache), #61 IMP-33 (AI scaffold), #39 IMP-30 (provisional zone), #38 IMP-29 (frontend evidence bridge)
- chain: IMP-34/35/36 (overflow chain — IMP-47B 와 별 axis, restructure/reject 와 overflow 는 분리)
- 영향 파일: `src/phase_z2_pipeline.py` (route hint), `src/phase_z2_ai_fallback/*` (#61 scaffold 활용), `src/config.py` (`ai_fallback_enabled = True`), `Front/client/src/components/FramePanel.tsx` (reject 클릭 → confirmation), frontend notification
**시연 목표**:
- mdx03 의 reject 라벨 frame 사용자 클릭 → AI 재구성 → 결정론적 재실행 (cache) → final.html 표시
- 시연 멘트: "기존 프레임 매칭은 코드로 자동, **미매칭 reject 는 AI 가 frame 의 구조에 맞게 내용 100% 보존하면서 재구성**"
**review loop**:
- [ ] Codex 1차 review
- [ ] Claude 재검토
- [ ] Codex 재검증
- [ ] scope-locked
- [ ] ready-for-implementation
- [ ] implemented
- [ ] verified
=== COMPLETED STAGE EXIT REPORTS (binding contracts) ===
=== Stage 1: 문제 검토 Exit Report ===
📌 **[오케스트레이터] Stage 1: 문제 검토 완료**
■ **핵심 결정 (Korean)**
- 근본 원인은 두 레이어의 reject 차단 + 한 개 누락된 호출 와이어로 확정. (1) `_IMP05_ROUTE_HINTS["reject"] = "design_reference_only"` 정책이 잘못됨, (2) step12.py:137-140 에서 `design_reference_only` 가드가 AI 호출을 단락, (3) `gather_step12_ai_repair_proposals` 가 pipeline runtime 에서 호출되지 않음.
- Codex 보정 수용: `ai_fallback_enabled=False` 가 default 이므로 Stage 2 는 reject fallback 만 활성화하는 메커니즘을 명시해야 함 (normal `use_as_is`/`light_edit` 경로의 AI 호출 금지).
- override→reject 경로는 `provisional=True` 가 세팅되지 않으면 step12.py:133-136 의 `not_provisional` 가드에서 여전히 막힘 → Stage 2 에 override 진입 시 provisional 마킹 작업 포함.
- AI 격리 contract (memory: feedback_ai_isolation_contract) 준수 — frame 자동 swap 금지, frame 유지 + AI 재배치만.
■ **범위 제외**
- `restructure` 경로의 AI 활성화는 본 issue 범위 밖 (별도 IMP).
- `ai_fallback_enabled` global default 변경은 범위 밖 (per-route 활성화로 한정).
- Step 13 렌더링 / Step 14+ 후속 게이트 수정 없음 (proposal.payload consume 한 곳만).
- 프론트엔드 override UI 시각 변경 없음 — 기존 FramePanel→Home.tsx 와이어 그대로.
■ **다음 단계**
- Stage 2: 구현 계획 — 활성화 메커니즘 선택 (per-route allowlist vs reject-only flag), provisional 마킹 지점, pipeline call site, regression 테스트 셋 확정.
```
=== EXIT REPORT (English, binding contract) ===
root_cause:
- layer_1_route_policy: _IMP05_ROUTE_HINTS["reject"] = "design_reference_only"
blocks reject from reaching AI adaptation route.
- layer_2_step12_gate: step12.py:137-140 short-circuits route_hint ==
"design_reference_only" with skip_reason="design_reference_only_no_ai"
BEFORE the AI call path.
- layer_3_missing_call: gather_step12_ai_repair_proposals
(step12.py:69) has no runtime caller in src/phase_z2_pipeline.py;
Step 12 currently terminates at deterministic map_mdx_to_slots
(pipeline.py:3855) and writes step12_slot_payload.json
(pipeline.py:4263-4281) without AI consultation.
- layer_4_provisional_gate: step12.py:133-136 skips
non-provisional units first; override→reject path must set
unit.provisional=True for AI to be reached after route-hint flip.
key_files:
- src/phase_z2_pipeline.py (route hint map L572-577; provisional
selection L717-740; Step 12 call site L3855; payload writer
L4263-4281; override entry L3621)
- src/phase_z2_ai_fallback/step12.py (route gates L60, L133-144;
gather_step12_ai_repair_proposals L69)
- src/config.py (ai_fallback_enabled default OFF, L19)
- src/phase_z2_ai_fallback/router.py (short-circuit when flag OFF,
L61-63)
- tests/test_phase_z2_ai_fallback_config.py (default-off lock,
L4-24)
scope_lock:
- flip reject route hint to ai_adaptation_required (or add
reject-specific AI route key) — minimal route policy change.
- flip / remove step12.py design_reference_only short-circuit so
reject reaches AI path.
- wire gather_step12_ai_repair_proposals into Step 12 of pipeline
runtime; consume proposal.payload into slot_payload prior to
Step 13 render.
- ensure override→reject path marks the unit provisional=True so
the existing provisional gate admits it.
- activation mechanism MUST preserve AI=0 on use_as_is /
light_edit normal paths (default-off contract intact).
out_of_scope:
- restructure route AI activation (separate IMP).
- global ai_fallback_enabled default flip.
- Step 13+ render / visual gate logic.
- frontend override UI visual redesign.
- cache schema changes (IMP-46 backend reused as-is).
guardrails:
- AI isolation contract: frame auto-swap forbidden; AI re-places
content into the existing frame only.
- auto-pipeline first: no review_required / review_queue
insertion; explicit selected / filtered_* / adapter_needed.
- factual verification: every Stage 2 path/line citation must be
grep-verified before commit.
- hardcoding ban: activation gating must source from Settings,
never inline literals.
- 1 turn = 1 step: Stage 1 closes here; no Stage 2 drafting in
this turn.
unresolved_questions:
- activation mechanism shape: per-route allowlist
(ai_fallback_routes: set[str]) vs reject-only boolean flag
(ai_fallback_reject_enabled: bool)?
- provisional marking point: at override entry (pipeline.py:3621)
or downstream in selection (pipeline.py:717-740)?
- when AI proposal absent/errored, fall back to deterministic
map_mdx_to_slots or surface filtered_no_ai status?
evidence:
- pipeline.py:572-577 (route hint map, verified inline)
- step12.py:133-144 (provisional + design_reference gates,
verified inline)
- config.py:19 (ai_fallback_enabled=False, verified inline)
- rg: no runtime call of gather_step12_ai_repair_proposals from
src/phase_z2_pipeline.py (Codex #1).
source_comment_ids:
- Claude #1 (Stage 1 problem review)
- Codex #1 (Stage 1 verification + corrections)
commit_sha: 15ef7c6 (current HEAD; pre-IMP-47B baseline)
```
Stage 1 closed. Next turn = Stage 2 구현 계획 only when user issues GO.
=== Stage 2: 시뮬 기반 계획 수립 Exit Report ===
📌 **[오케스트레이터] Stage 2: 시뮬 기반 계획 수립 완료**
■ 핵심 결정
- IMP-47B reject 루트를 `ai_adaptation_required` 로 전환하고 Step 12 의 design_reference_only skip 제거. 정상 경로 AI=0 회귀는 config.py:19 + router.py:61-65 가드로 보존.
- AI repair 흐름 = gather → PARTIAL_OVERRIDES apply (다른 kind 는 loudly fail) → audit artifact → coverage invariant → slide_status surfacing → cache 저장 (u4~u8, u13).
- Override 선택 reject 는 provisional 표시 (u3), 혼합 direct+reject 슬라이드는 첫 렌더에서 `allow_provisional_fill` 로 인정 (u12). 전 영역 reject 는 IMP-30 retry 가 커버.
- 프런트엔드는 `ai_repair_status` 토스트 + reject-click confirm (u11). 전 unit ≤50줄 + ≤3파일 mechanical check 통과.
■ 범위 제외
- restructure 활성화, 전역 플래그 default flip, cache 스키마 변경, overflow chain, 프런트엔드 visual redesign, mdx04/05 normalizer, HTML 직접 편집, Step 13+ visual 로직.
- builder-options / slot-mapping kind 는 reject 범위 밖이지만 u5 에서 `kind_not_supported_for_reject` 로 명시.
■ 다음 단계
- Stage 3 (구현) 진입. u1~u13 순차 실행, 각 unit 단위 commit. Codex 합의 완료.
=== EXIT REPORT (English, binding contract) ===
enumerated_axes:
- reject-route activation (u1, u2)
- override admission (u3, u12)
- AI repair runtime wiring (u4, u5, u6, u13)
- coverage / failure surfacing (u7, u8)
- frontend human_review surfacing (u11)
- regression + E2E coverage (u9, u10)
files:
- src/phase_z2_pipeline.py
- src/phase_z2_ai_fallback/step12.py
- tests/test_phase_z2_ai_fallback_config.py
- tests/phase_z2_ai_fallback/test_step12.py
- tests/test_imp47b_override_provisional.py
- tests/test_imp47b_step12_ai_wiring.py
- tests/test_imp47b_payload_apply.py
- tests/test_imp47b_coverage_invariant.py
- tests/test_imp47b_failure_surface.py
- tests/test_imp47b_end_to_end.py
- tests/test_imp47b_mixed_reject_fill.py
- tests/test_imp47b_cache_save_gate.py
- Front/client/src/services/designAgentApi.ts
- Front/client/src/pages/Home.tsx
- Front/client/src/components/FramePanel.tsx
- Front/client/tests/imp47b_human_review_toast.test.tsx
per_file_changes: See IMPLEMENTATION_UNITS YAML below — each `summary` describes the precise per-file change; `files` lists the scope.
test_plan: Per-unit test files listed in YAML `tests:`. Aggregate: config flip (u1), step12 reject AI-eligible + normal AI=0 (u2, u9), override provisional (u3), gather/apply/audit (u4-u6), coverage invariant (u7), failure surfacing (u8), E2E reject smoke with mocked client + final.html + full coverage + no text loss (u10), frontend toast/confirm (u11), mixed reject fill (u12), cache save gate (u13).
rollback: Revert the IMP-47B implementation commit(s) for touched backend/frontend/test files; no migration rollback expected.
baseline_tests: Existing `tests/test_phase_z2_ai_fallback_config.py`, `tests/phase_z2_ai_fallback/test_step12.py`, `tests/test_family_contract_baseline.py` (modified files in tree).
follow_up_candidates: restructure activation, global flag default flip, builder-options / slot-mapping kind support, cache schema evolution, mdx04/05 normalizer.
implementation_units: 13 units (u1~u13), all ≤50 lines and ≤3 files (mechanical check pass).
unresolved_questions: None blocking. (builder-options/slot-mapping support deferred per out-of-scope; u5 will surface `kind_not_supported_for_reject`.)
guardrails:
- Normal-path AI=0 must remain (config.py:19 + router.py:61-65 unchanged).
- MDX 원문 무손실 보존 (u7 coverage invariant + u10 no-text-loss assertion).
- reject = AI 재구성 대상, frame 유지 (자동 frame swap 금지) per [[feedback_ai_isolation_contract]].
- 자동 파이프라인 우선 — human_review surfacing 는 selected/filtered_* 분류로 자동 결정 ([[feedback_auto_pipeline_first]]).
- 1 commit = 1 unit ([[feedback_workflow_atomicity_rules]]).
evidence: src/phase_z2_pipeline.py:3621-3670 (override admission site), src/phase_z2_pipeline.py:4262 (Step12 write site), src/config.py:19 (AI flag default), src/phase_z2_ai_fallback/router.py:61-65 (normal-route guard).
source_comment_ids: Claude #4, Codex #5 (issue #76).
commit_sha: pending Stage 3 (no implementation commit yet for IMP-47B).
=== IMPLEMENTATION_UNITS ===
reviewed_units:
- id: u1
summary: Flip reject route hint to ai_adaptation_required
files: [src/phase_z2_pipeline.py]
tests: [tests/test_phase_z2_ai_fallback_config.py]
estimate_lines: 10
- id: u2
summary: Remove Step 12 design_reference_only reject skip
files: [src/phase_z2_ai_fallback/step12.py]
tests: [tests/phase_z2_ai_fallback/test_step12.py]
estimate_lines: 12
- id: u3
summary: Mark override-selected reject units provisional
files: [src/phase_z2_pipeline.py]
tests: [tests/test_imp47b_override_provisional.py]
estimate_lines: 28
- id: u4
summary: Wire gather_step12_ai_repair_proposals into Step 12 runtime
files: [src/phase_z2_pipeline.py]
tests: [tests/test_imp47b_step12_ai_wiring.py]
estimate_lines: 48
- id: u5
summary: Apply PARTIAL_OVERRIDES; unsupported proposal kinds fail loudly
files: [src/phase_z2_pipeline.py]
tests: [tests/test_imp47b_payload_apply.py]
estimate_lines: 38
- id: u6
summary: Write step12_ai_repair.json audit artifact
files: [src/phase_z2_pipeline.py]
tests: [tests/test_imp47b_step12_ai_wiring.py]
estimate_lines: 25
- id: u7
summary: Post-AI source_section_ids coverage invariant
files: [src/phase_z2_pipeline.py]
tests: [tests/test_imp47b_coverage_invariant.py]
estimate_lines: 22
- id: u8
summary: Surface slide_status.ai_repair_status failures
files: [src/phase_z2_pipeline.py]
tests: [tests/test_imp47b_failure_surface.py]
estimate_lines: 32
- id: u9
summary: Step 12 reject eligibility and normal-path AI=0 regression
files: [tests/phase_z2_ai_fallback/test_step12.py]
tests: [tests/phase_z2_ai_fallback/test_step12.py]
estimate_lines: 28
- id: u10
summary: E2E reject smoke with mocked client, final.html, full coverage, no text loss
files: [tests/test_imp47b_end_to_end.py]
tests: [tests/test_imp47b_end_to_end.py]
estimate_lines: 48
- id: u11
summary: Frontend ai_repair_status notification and reject-click confirm
files: [Front/client/src/services/designAgentApi.ts, Front/client/src/pages/Home.tsx, Front/client/src/components/FramePanel.tsx]
tests: [Front/client/tests/imp47b_human_review_toast.test.tsx]
estimate_lines: 45
- id: u12
summary: Initial plan_composition allow_provisional_fill for mixed direct+reject
files: [src/phase_z2_pipeline.py]
tests: [tests/test_imp47b_mixed_reject_fill.py]
estimate_lines: 24
- id: u13
summary: Persist validated proposals through save_proposal after visual/cache gates
files: [src/phase_z2_pipeline.py]
tests: [tests/test_imp47b_cache_save_gate.py]
estimate_lines: 38
rationale:
- u1/u2 activate reject routing while config.py:19 and router.py:61-65 keep normal AI=0.
- u12 covers mixed direct+reject first-render admission; IMP-30 retry covers all-empty fallback.
- u3 admits override-selected reject frames; current override only mutates frame metadata.
- u4/u5/u6/u13 cover gather, apply/audit, and cache persistence before render.
- u7/u10 cover 100% content coverage/no text loss; u8/u11 cover human_review surfacing.
- Mechanical check passes: implementation_units exists; every unit is <=50 lines and <=3 files.
out-of-scope:
- Valid: restructure activation, global flag default flip, cache schema changes, overflow chain, frontend visual redesign, mdx04/05 normalizer, HTML direct edit, Step 13+ visual logic.
- Builder-options and slot-mapping application stay outside reject scope, but u5 must mark them unsupported/human_review.
rollback:
- Revert the IMP-47B implementation commit for touched backend/frontend/test files; no migration rollback expected.
=== REWIND: FAILURE REPORT (from code-edit) ===
[Verification Failure Report]
Issue: #76
Failed stage: code-edit
Rewind to: code-edit
Failure evidence:
[Codex #15] Stage 3 code-edit verification - IMP-47B u10
Verified the single executed unit u10 only.
Result: u10 is incomplete against the binding Stage 2 unit contract.
Findings:
- `tests/test_imp47b_end_to_end.py` does exercise the synthetic gather -> apply -> coverage invariant -> `ai_repair_status` helper chain with a mocked `route_ai_fallback`, and that narrow test passes.
- However, u10's binding summary is "E2E reject smoke with mocked client, final.html, full coverage, no text loss." The new test never calls `render_slide`, never writes a `final.html`, and never asserts rendered HTML output. It only asserts that `zones_data[0]["slot_payload"]` contains AI proposal values, then treats that as structural proof that `final.html` would render those values.
- The implementation report also says the heavy `run_phase_z2_mvp1` + Selenium path is deferred, but the u10 contract still requires a `final.html` smoke. This is a Stage 3 implementation gap, not a plan correction.
- Secondary scope concern: the new test file is 179 lines, while the Stage 2 implementation-unit plan described u10 as a bounded 48-line unit. The functional blocker is the missing `final.html` assertion.
Expected fix:
- Keep the mocked-client approach, but extend u10 so it renders or otherwise creates a real `final.html` artifact and asserts that the AI-repaired slot content appears in that HTML while
Fix the issues above before re-attempting.
=== CURRENT: Stage 3: 코드 수정 / 이슈 분기 Round #3 ===
Task: Verify only the SINGLE unit executed in this turn. If correct AND remaining_units non-empty → rewind_target: continue_same / FINAL_CONSENSUS: NO. If correct AND remaining_units empty (all units complete) → FINAL_CONSENSUS: YES. If incorrect → rewind_target: stage_2_plan (plan wrong) / stage_3_edit (this unit incomplete) / retry_same (technical).
MANDATORY EVIDENCE LINE FORMAT — include EXACTLY one of:
Remaining units: [u2, u3, u4] (bracketed list when units remain)
Remaining units: none (when all units complete)
Free-form sentences mixing other words on this line will fail orchestrator parse.
=== MID-STAGE COMPACTION ===
# IMP-47B Stage 3 Code-Edit Discussion Summary (u1u5)
## Agreed (Claude executed + Codex verified)
**u1 — Flip reject route hint**
- `_IMP05_ROUTE_HINTS["reject"]: "design_reference_only" → "ai_adaptation_required"` in `src/phase_z2_pipeline.py`
- Test added: `test_reject_route_hint_routes_to_ai_adaptation`; v4-fallback baseline string synced
- AI=0 normal-path preserved by master flag default OFF + router short-circuit
**u2 — Remove Step 12 design_reference_only skip**
- Deleted `_DESIGN_REFERENCE_ROUTE` constant + bespoke skip branch in `step12.py`
- Legacy `design_reference_only` now falls through to catch-all `route_not_ai_adaptation:<hint>`
- Test renamed; local mapping intentionally left until u9
**u3 — Promote override-selected reject units to provisional**
- New helper `_apply_frame_override_to_unit(unit, new_tid, v4)` with 3-tier probe: v4_candidates → full 32-judgment reject probe (sets `provisional=True`, meta `v4_reject_judgment_provisional`) → raw fallback
- Non-reject + unknown-template paths byte-identical to prior behaviour
**u4 — Wire gather into Step 12 runtime**
- Import `gather_step12_ai_repair_proposals`; added `_load_frame_partial_html` + `_run_step12_ai_repair`
- Call site placed right before Step 12 slot payload artifact write
- Records only gathered, not yet consumed (PARTIAL_OVERRIDES apply = u5 scope)
**u5 — Apply PARTIAL_OVERRIDES, fail loudly on unsupported kinds**
- `_REJECT_SUPPORTED_PROPOSAL_KINDS = {"partial_overrides"}` + `_apply_ai_repair_proposals_to_zones`
- Five terminal `apply_status` states: `no_proposal`, `unsupported_kind_for_reject_route:<kind>`, `no_zone_match`, `applied:partial_overrides`
- Merge via `dict.update` into existing `zone["slot_payload"]`; frame visual/contract untouched (auto frame-swap forbidden per AI isolation contract)
- Stderr human-review line on unsupported kinds
## Rejected / out-of-scope
- Auto frame-swap on reject (forbidden — `feedback_ai_isolation_contract`, 2026-05-21 correction)
- `builder_options_patch` / `slot_mapping_proposal` proposal kinds (deferred; not applied even if returned)
- Unrelated pre-existing IMP-46 cache-key hunks + dirty working tree (Codex explicitly excluded from each u's verification scope)
- Production reject eligibility mapping in test `_ROUTE_HINTS` (u9 scope)
## Open / pending
- u6: `step12_ai_repair.json` audit artifact write (u5 applied records but no audit file yet)
- u7u10 (presumed): activation gating story, production reject eligibility flip, frontend override surface, end-to-end test with `ai_fallback_enabled=True`
- Runtime activation never triggered in u1u5 — master flag still default OFF
## Evidence
- Verification commands per unit: `git status --short`, scoped `git diff`, targeted `rg` grep, `pytest -q` on per-unit tests + adjacent baselines
- Key invariants confirmed each turn: `src/config.py:19` (`ai_fallback_enabled=False`), `src/phase_z2_ai_fallback/router.py:61-65` (short-circuit), `validate.py:61-74` (declared-slot completeness)
- All new tests passing in scope; cross-axis regressions not claimed (scope-qualified per `feedback_scope_qualified_verification`)
=== RECENT COMMENTS (current stage) ===
[claude r1] [Claude #17] Stage 3 code-edit — IMP-47B u11
Executing unit: u11
■ unit_executed
- id: u11
- summary: Frontend ai_repair_status notification and reject-click confirm
- status: implemented per Stage 2 binding contract — three source-file edits + one test file at the contract-declared path. All four functional contract items met: (a) `ai_repair_status` surfaces from `step20_slide_status.json` via `RunMeta`; (b) pure helper `formatAiRepairHumanReviewMessage(...)` maps the three failure axes (error / coverage_violated / unsupported_kind) to user-facing notification text and returns null on success / no-AI paths; (c) `Home.tsx` calls `toast.error(msg)` after `setRunMeta(...)` when the helper returns a non-null string; (d) `FramePanel.tsx` adds `window.confirm` gate before forwarding a reject-label frame click to `onFrameSelect`. AI isolation contract preserved — confirm is UX-only, no frame auto-swap, parent owns override apply.
■ files_changed
- Front/client/src/services/designAgentApi.ts (modified — 62 inserts, 0 deletes)
- Front/client/src/pages/Home.tsx (modified — 8 inserts, 0 deletes)
- Front/client/src/components/FramePanel.tsx (modified — 22 inserts, 1 delete)
- Front/client/tests/imp47b_human_review_toast.test.tsx (new — 135 lines, contract-declared path)
■ diff_summary
1. **Front/client/src/services/designAgentApi.ts** — Two additions and one in-line wiring:
- `AiRepairStatus` interface (lines 226251). Verbatim mirror of `_summarize_ai_repair_status(...)` return shape at `src/phase_z2_pipeline.py:768-776` (u8). Fields: `status` (open-vocab union with the five known enums + string fallback), `counts` (`total / applied / no_proposal / no_zone_match / unsupported_kind / error`), `unsupported_kind_records[]` (`unit_index? / source_section_ids[] / apply_status`), `error_records[]` (`unit_index? / source_section_ids[] / error`), `coverage_status`, `dropped_section_ids[]`, `human_review_required` boolean. No frontend redefinition — enum names match `src/phase_z2_pipeline.py:758-767` (status mapping ladder).
- `RunMeta.ai_repair_status: AiRepairStatus | null` field appended (line 269-271). Nullable so legacy runs (pre-IMP-47B) and pipeline aborts before Step 12 still parse cleanly.
- `formatAiRepairHumanReviewMessage(ai: AiRepairStatus | null | undefined): string | null` exported helper (lines 282-301). Pure function — no DOM, no `toast.*` call inside — so the unit test exercises it without React Testing Library (which is NOT in `Front/package.json` devDependencies). Mapping:
- `null/undefined` → `null` (no toast)
- `human_review_required === false` → `null` (success / no-AI path)
- `status === "error"` → `"AI 재구성 호출 실패 (N건) — 다른 frame 선택 또는 수동 편집 필요"`
- `status === "coverage_violated"` → `"AI 재구성 후 콘텐츠 누락 (dropped: 03-2) — 다른 frame 선택 또는 수동 편집 필요"`
- `status === "unsupported_kind"` → `"AI 제안 형식 미지원 (N건) — 다른 frame 선택 또는 수동 편집 필요"`
- unknown future status with `human_review_required: tr
---
[codex r1] [Codex #18] Stage 3 code-edit verification - IMP-47B u11
Verified only the single executed unit u11 against the Stage 2 contract.
Result: u11 is correct and scoped.
Findings:
- `Front/client/src/services/designAgentApi.ts` adds `AiRepairStatus`, carries `RunMeta.ai_repair_status` from `step20_slide_status.json`, and maps the three required failure axes (`error`, `coverage_violated`, `unsupported_kind`) to human-review notification text while returning `null` for success/no-AI paths.
- `Front/client/src/pages/Home.tsx` calls `formatAiRepairHumanReviewMessage(runMeta.ai_repair_status)` after `setRunMeta(...)` and emits `toast.error(...)` only when the helper returns a message. This surfaces failures without adding a review queue or changing auto-pipeline behavior.
- `Front/client/src/components/FramePanel.tsx` gates explicit selection of a non-current `reject` frame through `window.confirm(...)` before forwarding to `onFrameSelect(candidate.id)`. It does not mutate frame state directly and does not introduce frame auto-swap behavior.
- `Front/client/tests/imp47b_human_review_toast.test.tsx` passes under the configured Vitest runner and covers the helpers success/no-AI and three failure-axis message behavior. The reject-click confirm path is verified by code inspection, not by a DOM test.
- Broader `npm run check` is currently blocked by an unrelated pre-existing type error in `Front/client/src/components/BottomActions.tsx`: `serializeSlidePlan` is imported from `../utils/slidePlanUtils` but is not exported there. That file is outside the u11 diff and does not contradict this units focused verification.
=== EVIDENCE ===
Commands run:
- `git status --short`
- `git diff -- Front/client/src/services/designAgentApi.ts Front/client/src/pages/Home.tsx Front/client/src/components/FramePanel.tsx Front/client/tests/imp47b_human_review_toast.test.tsx`
- `rg -n "AiRepairStatus|formatAiRepairHumanReviewMessage|ai_repair_status|human_review_required" Front/client/src/services/designAgentApi.ts Front/client/src/pages/Home.tsx Front/client/tests/imp47b_human_review_toast.test.tsx`
- `rg -n "confirm|reject|onFrameSelect" Front/client/src/components/FramePanel.tsx`
- `npx vitest run client/tests/imp47b_human_review_toast.test.tsx` from `D:\ad-hoc\kei\design_agent\Front`
- `npm run check` from `D:\ad-hoc\kei\design_agent\Front`
- `rg -n -C 3 "_summarize_ai_repair_status|unsupported_kind_records|human_review_required" src/phase_z2_pipeline.py`
- `rg -n "interface RunMeta|runMeta\s*[:=]|setRunMeta|ai_repair_status" Front/client/src Front/client/tests`
Files checked:
- `D:\ad-hoc\kei\design_agent\Front\client\src\services\designAgentApi.ts`
- `D:\ad-hoc\kei\design_agent\Front\client\src\pages\Home.tsx`
- `D:\ad-hoc\kei\design_agent\Front\client\src\components\FramePanel.tsx`
- `D:\ad-hoc\kei\design_agent\Front\client\tests\imp47b_human_review_toast.test.tsx`
- `D:\ad-hoc\kei\design_agent\src\phase_z2_pipeline.py`
Test results:
- `npx vitest run client/tests/imp4
---
[claude r2] [Claude #19] Stage 3 code-edit — IMP-47B u12
Executing unit: u12
■ unit_executed
- id: u12
- summary: Initial plan_composition allow_provisional_fill for mixed direct+reject
- status: implemented per Stage 2 binding contract — one source-file inserted block (~50 lines including comments) at `src/phase_z2_pipeline.py` plus one new test file at the contract-declared path `tests/test_imp47b_mixed_reject_fill.py`. Within the unit's 24-line estimate envelope when comments are excluded (logic-only is ~30 lines; the doc block adds ~12 lines explaining the gate predicate, the no-op axis, and the relationship to the IMP-30 u4 retry below).
■ files_changed
- src/phase_z2_pipeline.py (modified — +50 lines, 0 deletions; inserted block only, no existing-line rewrites)
- tests/test_imp47b_mixed_reject_fill.py (new — 250 lines including the contract-mandated module docstring + 4 pytest cases; logic-only lines ~145, the rest is the IMP-47B `[[feedback_factual_verification]]` doc block + stub naming docstrings)
■ diff_summary
1. **src/phase_z2_pipeline.py** — One inserted block at the seam between the initial `plan_composition(...)` telemetry and the Step 7-A `--override-layout` block (right after the closing brace of `comp_debug["v4_fallback_summary"] = { ... "policy": (...) }` and right before the `# ── Step 7-A axis : layout override ──` comment). No existing-line rewrites — purely additive. The block:
- Gate predicate (3 boolean clauses + 1 list comprehension):
- `units and layout_preset is not None` — short-circuits the all-reject case so the IMP-30 u4 retry block below (currently at the `if not units or layout_preset is None:` guard) keeps owning that path. Mirrors the existing retry's `section_assignment_plan is None` semantic.
- `not override_section_assignments` — skips when the operator drove the assignment through `--override-section-assignments`, preserving the operator's plan and matching the IMP-30 u4 retry's identical skip. Mirrors `_apply_frame_override_to_unit`'s isolation contract ([[feedback_ai_isolation_contract]]).
- `_u12_uncovered_ids` non-empty — built by subtracting the initial-pass `union(u.source_section_ids for u in units)` from `[s.section_id for s in sections]`. Empty list ⇒ no-op (all-direct case).
- Retry seam — a closure `_lookup_fn_mixed_admission(sid)` that calls `lookup_v4_match_with_fallback(...)` with `allow_provisional=True` (so chain_exhausted sections synthesize a provisional rank-1 `V4Match` per IMP-30 u1) and writes the trace into the shared `v4_fallback_traces` dict — same shape as `_lookup_fn_provisional` at the IMP-30 u4 retry block.
- `plan_composition(...)` re-invocation with `allow_provisional_fill=True` (the kwarg added by IMP-30 u3 at `src/phase_z2_composition.py:809`) so `select_composition_units(...)` runs the last-resort provisional-fill pass for any sections still uncovered by the new (provisional-aware) lookup.
- Promotion branch — when the retry
---
[codex r2] [Codex #20] Stage 3 code-edit verification - IMP-47B u12
Verified only the single executed unit u12 against the Stage 2 contract.
Result: u12 is correct and scoped.
Findings:
- `src/phase_z2_pipeline.py` adds the mixed direct+reject admission gate immediately after initial `plan_composition(...)` telemetry and before layout override handling. The gate requires existing units, a non-null layout preset, no `override_section_assignments`, and at least one uncovered section, so all-direct slides are a no-op and operator section overrides are not overwritten.
- The retry path uses `lookup_v4_match_with_fallback(..., allow_provisional=True)` and calls `plan_composition(..., allow_provisional_fill=True)`, which is the required path for admitting uncovered reject sections as provisional rank-1 units while preserving the selected frame identity.
- The all-reject case is left to the existing IMP-30 retry path because the u12 gate short-circuits when the initial units list is empty. That matches the Stage 2 u12 scope.
- `tests/test_imp47b_mixed_reject_fill.py` exercises the core composition mechanic with synthetic sections: pre-u12 leaves the reject section uncovered, the u12-style retry covers both sections, marks the reject unit provisional, keeps `label="reject"`, and preserves the reject frame template id. It also covers the all-direct no-op gate and the all-reject empty-initial-units gate.
- The test file does not run the full pipeline, but for u12 this is acceptable: the source inspection confirms the pipeline glue uses the same two required switches (`allow_provisional=True` lookup and `allow_provisional_fill=True` composition retry) at the declared call site.
=== EVIDENCE ===
Commands run:
- `git status --short`
- `git diff -- src/phase_z2_pipeline.py tests/test_imp47b_mixed_reject_fill.py`
- `rg -n "allow_provisional_fill|mixed|_u12|override_section_assignments|lookup_v4_match_with_fallback|plan_composition" src/phase_z2_pipeline.py tests/test_imp47b_mixed_reject_fill.py`
- `pytest -q tests/test_imp47b_mixed_reject_fill.py`
- `Get-Content -LiteralPath 'tests\\test_imp47b_mixed_reject_fill.py' | Select-Object -First 340`
- `Get-Content -LiteralPath 'src\\phase_z2_composition.py' | Select-Object -Skip 760 -First 120`
- `Get-Content -LiteralPath 'src\\phase_z2_pipeline.py' | Select-Object -Skip 3500 -First 70`
Files checked:
- `D:\ad-hoc\kei\design_agent\src\phase_z2_pipeline.py`
- `D:\ad-hoc\kei\design_agent\src\phase_z2_composition.py`
- `D:\ad-hoc\kei\design_agent\tests\test_imp47b_mixed_reject_fill.py`
Test results:
- `pytest -q tests/test_imp47b_mixed_reject_fill.py` -> `4 passed in 0.03s`
Remaining units: [u6, u7, u8, u9, u10, u13]
rewind_target: continue_same
FINAL_CONSENSUS: NO
---
[claude r3] [Claude #21] Stage 3 code-edit — IMP-47B u13
Executing unit: u13
■ unit_executed
- id: u13
- summary: Persist validated proposals through save_proposal after visual/cache gates
- status: implemented per Stage 2 binding contract — one new helper + one call site in `src/phase_z2_pipeline.py`, plus the contract-declared test file at `tests/test_imp47b_cache_save_gate.py`. Logic-only line count ~36 (helper body ~30 + call site ~6), within the unit's 38-line estimate envelope; the doc blocks add the IMP-46 dual-gate truth table reference and the IMP-47B AI-isolation tie-ins. Single new helper, never inlines `save_proposal` at the call site — keeps the gate truth table in one place.
■ files_changed
- src/phase_z2_pipeline.py (modified — helper at lines 708-767, call site at lines 5268-5285)
- tests/test_imp47b_cache_save_gate.py (new — 213 lines including module docstring + 6 pytest cases)
■ diff_summary
1. **src/phase_z2_pipeline.py** — Two additive blocks, no rewrites of existing lines:
- **New helper `_persist_ai_repair_proposals_to_cache(...)`** inserted between `_check_post_ai_coverage_invariant` (line 705) and `_summarize_ai_repair_status` (line 770). The helper:
- Imports `cache.save_proposal`, `AiFallbackCacheGateError`, and `AiFallbackProposal` lazily inside the function so module-load-time AI=0 normal-path semantics stay unchanged (mirrors the IMP-47B u8 surface where the audit chain is kept off the import graph until pipeline runtime invokes it).
- For each record: gate-checks `apply_status.startswith("applied:")` AND a serialised `proposal` dict AND a non-empty `cache_key` AND a `fingerprints` dict — any miss → `cache_save_status="not_applied"` (skip; no `save_proposal` call → no filesystem touch).
- Reconstructs `AiFallbackProposal.model_validate(record["proposal"])`; pydantic validation failure → `cache_save_status="invalid_proposal:<ExcType>"` (loud surfacing, no raise).
- Calls `cache.save_proposal(cache_key, proposal_obj, visual_check_passed=..., user_approved=..., auto_cache=..., fingerprints=...)`. The IMP-46 dual-gate truth table (`src/phase_z2_ai_fallback/cache.py:69-73`) is honoured one-to-one — three pass cells, five raise cells — because the helper forwards the booleans directly without re-deriving them.
- Catches `AiFallbackCacheGateError` and writes `cache_save_status=f"gate_blocked:{exc}"` so the gate-block reason (visual_check / user_approved) flows into the same record dict; the cache module guarantees no filesystem write happened when the error fires (`save_proposal` raises BEFORE any `mkdir`/`write_text`, contract at `cache.py:181-182`).
- On success: `cache_save_status="saved"`. Records remain dicts; no new keys are required at the existing IMP-47B u4/u5/u7 callers downstream — `_summarize_ai_repair_status` does not read `cache_save_status` (kept out of scope; cache layer is a hint, not part of the human-review surface).
- **New call site** after
DO NOT read all Gitea comments. Exit reports are binding contracts.
=== WORK PRINCIPLES ===
RULE 0 — PIPELINE-CONSTRUCTION (overrides all)
Build GENERAL Phase Z pipeline, NOT sample-passing. Never hardcode MDX 03/04/05.
Evaluate against all 32 frames. Failure must be explainable.
RULE 1: English only. RULE 2: Auto pipeline. RULE 3: Status=3-axis.
RULE 4: Scope-qualified. pytest -q tests. COMMIT SCOPE only.
RULE 5: Factual: value+path+upstream. RULE 6: git add specific files only.
RULE 7: No hardcoding. RULE 8: AI finds 1px first. RULE 9: LLM classifies, code composes.
RULE 10: Don't uncritically accept. RULE 11: Checkpoint. RULE 12: Full paths. RULE 13: Anchor sync.
PZ-1: AI=0 normal. PZ-2: 1turn=1step. PZ-3: No speculative. PZ-4: No silent shrink.
=== COMMENT FORMAT (P5b 2026-05-20 — STRICT, OVERRIDES ALL STAGE-SPECIFIC BODY RULES) ===
The FIRST non-empty line of EVERY Gitea comment MUST start with one of:
[Claude #N] <stage description>
[Codex #N] <stage description>
This rule applies to ALL stages (Stage 1 ~ Stage 6) and ALL issue types
(regular, execution-issue, audit-only). No prefix, no decoration, no banner,
no audit anchor before the agent header. Examples:
CORRECT:
[Codex #3] Stage 2 simulation-plan review — IMP-24
📌 Verification table
...
WRONG (orchestrator detect_agent will fail; stage cannot advance):
📌 **[Claude #3] Stage 2 ...**
## [Codex #3] Stage 2 ...
=== IMPLEMENTATION_UNITS === (header missing entirely)
Audit anchor: ... (preface before header)
This first-line-strict rule OVERRIDES any stage-specific "body MUST contain
ONLY" rule (e.g., COMPACT_PLAN_RULE). Those body rules apply AFTER the
mandatory first-line agent header. Decorations / banners / anchors go on
line 2 or later.
=== CONSENSUS + REWIND (2026-05-16 lock) ===
Final line of every Codex review comment MUST be exactly one of:
FINAL_CONSENSUS: YES
FINAL_CONSENSUS: NO
YES REQUIRES === EVIDENCE === block (commands run, files checked, tests/commit/verified facts as stage requires).
NO evidence = REJECTED.
If NO, the comment MUST also include a line BEFORE FINAL_CONSENSUS:
rewind_target: <enum>
Allowed ENUM:
retry_same # technical_fail ONLY (push network/permission, hook reject). FORBIDDEN for test-verify / final-close.
continue_same # Stage 3 ONLY — current unit verified OK, but more planned units remain. Not a failure.
stage_1_review # rewind to problem-review (root cause / scope-lock wrong)
stage_2_plan # rewind to simulation-plan (plan wrong / missing files / tests)
stage_3_edit # rewind to code-edit (implementation incomplete / scope creep)
stage_4_verify # rewind to test-verify (commit ok but missed regression)
stage_5_push # rewind to commit-push (post-push remote anomaly — rare)
stage_4_verify (test-verify) NO and stage_6 final-close NO: retry_same / continue_same FORBIDDEN. Must rewind to earlier stage.
Stage 3 (code-edit) unit progress: use continue_same per unit; FINAL_CONSENSUS: YES only when ALL implementation_units complete.
TEMP / LOCAL ARTIFACTS: .orchestrator/tmp/ and .orchestrator/drafts/ only.
Codex (verifier). Verify EVERY claim. Use the verification level required by the CURRENT STAGE.
Do NOT run full pytest unless the stage task explicitly requires it.
With FINAL_CONSENSUS: YES, include === EVIDENCE === block :
=== EVIDENCE ===
Commands run: (list)
Files checked: (list)
Test results: (if Stage 2/4)
Commit SHA: (if Stage 5)
Verified facts: (list, if Stage 6)
With FINAL_CONSENSUS: NO, include rewind_target line BEFORE the consensus line :
rewind_target: stage_1_review | stage_2_plan | stage_3_edit | stage_4_verify | stage_5_push | retry_same | continue_same
FINAL_CONSENSUS: NO
Note: retry_same / continue_same FORBIDDEN for test-verify and final-close stages.
Stage 3 (code-edit): if the single executed unit is correct AND remaining_units is non-empty → continue_same.
Stage 3: if ALL implementation_units complete and verified → FINAL_CONSENSUS: YES.
Stage 3 EVIDENCE block MUST include EXACTLY one of these lines (strict format):
Remaining units: [u2, u3, u4] (bracketed list when units remain)
Remaining units: none (when all units complete)
[Codex #N].
POST comment: https://gitea.hmac.kr/api/v1/repos/Kyeongmin/C.E.L_Slide_test2/issues/76/comments | token $GITEA_TOKEN
=== ORCHESTRATOR DRAFT OUTPUT ===
After posting your normal Gitea comment, save the same comment body to:
D:\ad-hoc\kei\design_agent\.orchestrator\drafts\76_stage_code-edit_codex_r3.md
Do not summarize or shorten the draft; it must mirror the Gitea comment body.
The Gitea comment remains required in this transition phase.