untracked files on main: dceb101 feat(#63): IMP-34 R1 donor capacity measured bound (u1+u2)

This commit is contained in:
kmlee1 committed 2026-05-21 22:07:41 +09:00
commit 8f085a28d3
3220 files changed
+985495

No files matched your search

+1
View File
@@ -0,0 +1 @@
{"body": "[Claude #14] Stage 3 code-edit — IMP-47B u10\n\nExecuting unit: u10\n\n■ unit_executed\n- id: u10\n- summary: E2E reject smoke with mocked client, final.html, full coverage, no text loss\n\n■ files_changed\n- tests/test_imp47b_end_to_end.py (new file)\n\n■ diff_summary\n- tests/test_imp47b_end_to_end.py (new file, ~165 lines including header / docstrings / blank lines)\n - **Module docstring** (Stage 2 lock surface): declares the slice scope as a synthetic E2E chain that wires together the four IMP-47B helpers introduced by u4 / u5 / u7 / u8 in the exact order `run_phase_z2_mvp1` invokes them between the Step 12 slot_payload write and the Step 20 slide_status attach (`src/phase_z2_pipeline.py` — u4 call site / u5 apply / u6 artifact write / u7 invariant / u8 surface). Documents that `render_slide(slide_title, slide_footer, zones_data, layout_preset, layout_css)` at `src/phase_z2_pipeline.py:5107` consumes `zones_data[i][\"slot_payload\"]` verbatim to emit `final.html` at `src/phase_z2_pipeline.py:5110-5111`, so asserting the post-apply slot_payload carries the AI proposal values is the structural proof that the reject route now flows into the rendered HTML. The docstring explicitly defers the heavy `run_phase_z2_mvp1` + Selenium overflow variant — this smoke test only exercises the deterministic chain. Five IMP-47B policy guardrails are enumerated and proven by the body assertions:\n * AI 호출 = fallback path only (master flag default OFF preserved — monkeypatch confines the seam swap to this test scope, no `settings.ai_fallback_enabled` write).\n * MDX 원문 100% 보존 (coverage_invariant.status == \"ok\", source_section_ids identical before/after AI).\n * 자동 frame swap 금지 (frame_template_id unchanged across gather + apply).\n * frame visual 임의 변경 금지 (frame_contract / partial untouched; apply only merges `proposal.payload.slots` into `slot_payload`).\n * dropped 절대 룰 (AI keys merged on top of deterministic keys; pre-existing meta keys survive).\n - **`_StubUnit` dataclass** (synthetic CompositionUnit stand-in — MOCK_ prefix per `tests/test_imp47b_step12_ai_wiring.py` + `tests/test_imp47b_override_provisional.py` convention, no real catalog template_id / frame_id leakage per Codex #7 generalization guardrail). Fields cover the subset that `gather_step12_ai_repair_proposals` reads from each unit: `label=\"reject\"` + `provisional=True` (triggers the AI-eligible code path), `frame_template_id=\"MOCK_T_reject\"` + `frame_id=\"MOCK_F_reject\"` + `source_section_ids=[\"MOCK_S1\"]` + `raw_content=\"MOCK MDX paragraph that must survive AI repair.\"`, plus the 6 structural axes (`v4_rank` / `cardinality` / `layout_preset` / `zone_position` / `source_shape` / `h3_count` / `char_count`) the cache-key signature builder reads at `src/phase_z2_ai_fallback/step12.py:160-178`. Default values are sane so individual tests can override only what they need.\n - **`_patched_route_ai_fallback(**kwargs)` deterministic stand-in** for `route_ai_fallback`: returns a real `AiFallbackProposal(proposal_kind=ProposalKind.PARTIAL_OVERRIDES, payload={\"slots\": {\"title\": \"AI repaired title\", \"bullets\": [\"AI repaired bullet 1\", \"AI repaired bullet 2\"]}}, rationale=\"E2E smoke proposal — deterministic.\")`. The docstring notes that the validator at `src/phase_z2_ai_fallback/validate.py:61-74` is bypassed because this seam replaces the router itself (not the client) — the structural slot completeness is asserted by the apply step + the coverage invariant downstream so the test does not re-implement validation. The proposal kind matches the only `_REJECT_SUPPORTED_PROPOSAL_KINDS` member (`partial_overrides`) from u5 so the apply path lands on `applied:partial_overrides` rather than the `unsupported_kind_for_reject_route:<kind>` fail-loud branch.\n - **Test 1 — `test_e2e_reject_chain_applies_proposal_and_preserves_coverage(monkeypatch)`** (~55 lines incl. docstring + comments). Drives the four IMP-47B u4 / u5 / u7 / u8 helpers in pipeline order with a single reject + provisional unit. Steps:\n 1. `monkeypatch.setattr(src.phase_z2_ai_fallback.step12, \"route_ai_fallback\", _patched_route_ai_fallback)` — patches the router seam imported by step12 at `src/phase_z2_ai_fallback/step12.py:59` so the gather call returns a deterministic proposal without touching the master flag / network / cache layers. PZ-1 invariant intact: the global `settings.ai_fallback_enabled` is not flipped; the test is hermetic.\n 2. `records = _run_step12_ai_repair(units)` → asserts `len(records) == 1`, `route_hint == \"ai_adaptation_required\"` (u1 production map proves reject routes to AI), `ai_called is True`, `skip_reason is None`, `proposal[\"proposal_kind\"] == \"partial_overrides\"`, `source_section_ids == [\"MOCK_S1\"]`. Locks the u4 wiring: eligible reject reaches the patched router and returns a real proposal.\n 3. `_apply_ai_repair_proposals_to_zones(records, Line truncated