feat(#61): IMP-33 AI fallback scaffolding (u1~u11, flag default OFF)

Frame-aware AI fallback module scaffolded under src/phase_z2_ai_fallback/
with master flag ai_fallback_enabled=False; normal-path AI call count
remains 0. AI output constrained to builder_options_patch /
partial_overrides / slot_mapping_proposal; MDX / frame_id / raw HTML /
raw CSS mutations rejected at schema layer. IMP-46 cache gate (cache.py)
raises AiFallbackCacheGateError unless visual_check_passed AND
user_approved. Step 12 wires AI repair after IMP-30 provisional payload
only; Step 17 stays blocked behind IMP-34 / IMP-35 prerequisites.
AST isolation guard forbids fallback package from importing Phase Q /
Kei / pipeline runtime symbols. Docs IMP-17 / IMP-31 bound to runtime
module surface via 11-row structural test pin (test_docs_sync.py) so
drift fails CI.

Tests: 116 fallback / 161 phase_z2 regression / 526 scoped full sweep
all passing. Existing pre-IMP-33 fixture issue in scripts/test_phase_t_*
remains untouched (out of scope).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
kmlee1andClaude Opus 4.7 committed 2026-05-21 12:46:49 +09:00
1 parent c412f1ea75
commit c864fe0479
24 files changed
+2119 -5

No files matched your search

+80
View File
@@ -0,0 +1,80 @@
"""IMP-33 u3 — AI fallback prompt builder (fallback path only).
System+user prompt for the Anthropic client (u4). MDX is READ-ONLY
(`feedback_ai_isolation_contract`); output is constrained to the u2
schema; frame_id swap is forbidden (V4 rank-1 protected,
`feedback_phase_z_spacing_direction`). Inputs per Stage 2 plan: V4
result (route=ai_adaptation_required, cardinality), frame_contract,
frame_visual HTML, figma_to_html_agent partial JSON, Internal Region,
MDX text.
"""
from __future__ import annotations
import json
from typing import Any
from src.phase_z2_ai_fallback.schema import FORBIDDEN_KINDS, ProposalKind
V4_ROUTE_AI_ADAPTATION = "ai_adaptation_required"
_ALLOWED_KINDS = ", ".join(sorted(k.value for k in ProposalKind))
_FORBIDDEN_KINDS = ", ".join(sorted(FORBIDDEN_KINDS))
SYSTEM_PROMPT = (
"You are an IMP-33 AI fallback adapter for Phase Z slide composition.\n"
"STRICT RULES:\n"
" 1. MDX text in the user payload is READ-ONLY. Do NOT rewrite, "
"compress, or paraphrase MDX.\n"
" 2. Output MUST be a single JSON object conforming to AiFallbackProposal.\n"
f" 3. proposal_kind MUST be one of: {_ALLOWED_KINDS}.\n"
f" 4. Do NOT propose any of: {_FORBIDDEN_KINDS}.\n"
" 5. Do NOT change frame_id — V4 rank-1 frame is locked.\n"
" 6. Keep declared frame slots (text/table/image/details) populated.\n"
" 7. Respect Internal Region containment; place content units within "
"the declared region only."
)
def build_ai_fallback_prompt(
*,
v4_result: dict[str, Any],
frame_contract: dict[str, Any],
frame_visual_html: str,
figma_partial_json: dict[str, Any],
internal_region: dict[str, Any],
mdx_text: str,
) -> dict[str, str]:
"""Build system+user prompt strings for the fallback AI adapter.
Raises:
ValueError: when ``v4_result.route`` is not
``ai_adaptation_required`` — the fallback prompt MUST NOT be
built outside this route (normal-path AI call count must
remain 0; PZ-1).
"""
route = v4_result.get("route") or v4_result.get("imp05_route_hint")
if route != V4_ROUTE_AI_ADAPTATION:
raise ValueError(
f"build_ai_fallback_prompt: v4_result.route={route!r} is not "
f"{V4_ROUTE_AI_ADAPTATION!r}; fallback prompt MUST NOT be built "
"outside the AI adaptation route."
)
user_payload = {
"v4": {
"route": route,
"cardinality": v4_result.get("cardinality")
or v4_result.get("cardinality_signature"),
"label": v4_result.get("label"),
"frame_id": v4_result.get("frame_id"),
"rank": v4_result.get("rank"),
},
"frame_contract": frame_contract,
"frame_visual_html": frame_visual_html,
"figma_partial_json": figma_partial_json,
"internal_region": internal_region,
"mdx_text_READ_ONLY": mdx_text,
}
return {
"system": SYSTEM_PROMPT,
"user": json.dumps(user_payload, ensure_ascii=False),
}