feat(#64): IMP-35 details_popup_escalation u1~u10 + Stage 3 R7 anchor re-pin
Land the production + test surface for the Step 17 cascade POPUP terminal (DETERMINISTIC -> POPUP -> AI_REPAIR -> USER_OVERRIDE) per Stage 2 plan R2. u11 (baseline-red invariance gate) was already landed in7c93031ahead of this commit; this commit completes u1~u10 plus the Stage 3 R7 follow-up anchor re-pin for test_imp17_comment_anchor.py. Implementation units (Stage 2 R2 contract): u1 frame_reselect_insufficient failure_type + post-frame remeasure (q4) - src/phase_z2_failure_router.py, src/phase_z2_pipeline.py u2 NEXT_ACTION_BY_FAILURE row + impl_status flip - src/phase_z2_failure_router.py u3 Router details_popup_escalation MISSING->IMPLEMENTED + executor stub - src/phase_z2_router.py u4 step17.py AI split-decision contract (POPUP cascade_stage + route_for_label + skip_reason); API gated - src/phase_z2_ai_fallback/step17.py u5 Step 17 POPUP gate executor; popup_escalation_plan + has_popup marker - src/phase_z2_pipeline.py, src/phase_z2_ai_fallback/step17.py u6 Composition popup binding -- yaml strategy -> zone payload - src/phase_z2_composition.py u7 Pipeline composer -> render_slide wiring (popup_html / preview_text / has_popup) - src/phase_z2_pipeline.py u8 slide_base.html <details>/<summary> popup wrapper - templates/phase_z2/slide_base.html u9 display_strategies.yaml inline_preview + popup metadata - templates/phase_z2/regions/display_strategies.yaml u10 MDX preservation invariant: popup=full source / body=summary or subset (asserted by tests/phase_z2/test_popup_mdx_preservation.py) u11 (already in7c93031) -- baseline-red invariance gate Stage 3 R7 follow-up (anchor re-pin, test-only): - tests/orchestrator_unit/test_imp17_comment_anchor.py Pre-anchor additions in src/phase_z2_pipeline.py (u1 / u5 / u7) shifted the restructure/reject route-hint comments 578/579 -> 586/587. Re-pinned the two guard tests (and docstring re-pin lineage 564 -> 570 -> 578 -> 586). Production code untouched. Verification (Stage 4 R1): pytest -q tests/orchestrator_unit/test_imp17_comment_anchor.py -> 2 passed / 0.02s pytest -q <10 IMP-35 unit files in tests/phase_z2 + tests/phase_z2_ai_fallback> -> 136 passed / 15.94s Baseline-red invariance gate (tests/test_imp47b_step12_ai_wiring.py + tests/test_phase_z2_ai_fallback_config.py) -> 4 failed / 6 passed; FAILED set === IMP35_BASELINE_RED_NODE_IDS (frozen registry from7c93031). Contract holds. Codex Stage 4 R1 = YES (independent verify). Guardrails honored: - MDX content preservation: popup carries full source, body holds summary or subset only (CLAUDE.md 자세히보기 원칙; feedback_phase_z_spacing_direction -- capacity expanded, no margin shrink). - AI isolation contract: Step 17 POPUP gate is deterministic; AI hook surface is split-decision contract only, API call gated. - No hardcoding: escalation thresholds derived from existing overflow detector outputs; preview_chars deterministic from container px. - 1 commit = 1 decision unit: u1~u10 land together as the planned production surface; u11 was deliberately split into7c93031as Stage 3 R7 carve-out, and the R7 anchor re-pin rides with this commit because it is the direct shift consequence of the u1/u5/u7 pre-anchor additions. - Scope-locked: .claude/settings.json explicitly excluded (Stage 4 exit report contract). Out of scope (per Stage 1 + Stage 2): - AI_REPAIR API activation (post IMP-35 axis). - IMP-34 zone resize, IMP-36 responsive fit (chain partners, separate issues). - Print-time auto-expand JavaScript for <details>. - Popup escalation in stages other than Step 17. - Baseline-red body repair (4 frozen failures) -- separate follow-up issue; u11 only guards the count. - frame_reselect algorithm changes (entry point only). - templates/phase_z2/slide_base.html path rename. source_comment_ids: Stage 1: claude_stage1_problem_review_imp35, codex_stage1_verification_imp35_yes Stage 2: Claude #4 R2 plan, Codex #5 R2 YES Stage 3: Claude #86 (R7 anchor re-pin), Codex #87 YES Stage 4: Claude #88 R1, Codex #89 R1 YES Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -290,6 +290,71 @@
|
||||
font-family: monospace;
|
||||
z-index: 3;
|
||||
}
|
||||
|
||||
/* ── IMP-35 u8 : popup details/summary (Step 17 POPUP gate escalation) ──
|
||||
When the Step 17 POPUP gate escalates a unit (zone.has_popup=True),
|
||||
slide_base renders a JS-free <details>/<summary> wrapper in the zone.
|
||||
The body of the frame stays as zone.partial_html (the FIT-version of
|
||||
content); the popup body holds the FULL original raw_content (MDX 원문
|
||||
무손실 보존 — 오답노트 #5 / IMPROVEMENT-REDESIGN.md §3.6 line 110).
|
||||
Placement (default top-right) is read from
|
||||
zone.popup_binding.detail_trigger.placement
|
||||
(templates/phase_z2/regions/display_strategies.yaml). HTML-native
|
||||
<details> per CLAUDE.md 자세히보기 contract — no JavaScript. */
|
||||
.zone__popup-details {
|
||||
position: absolute;
|
||||
z-index: 5;
|
||||
font-family: 'Pretendard', sans-serif;
|
||||
}
|
||||
.zone__popup-details--top-right {
|
||||
top: 4px;
|
||||
right: 4px;
|
||||
}
|
||||
.zone__popup-details--top-left {
|
||||
top: 4px;
|
||||
left: 4px;
|
||||
}
|
||||
.zone__popup-details--bottom-right {
|
||||
bottom: 4px;
|
||||
right: 4px;
|
||||
}
|
||||
.zone__popup-details--bottom-left {
|
||||
bottom: 4px;
|
||||
left: 4px;
|
||||
}
|
||||
.zone__popup-summary {
|
||||
list-style: none;
|
||||
cursor: pointer;
|
||||
padding: 2px 6px;
|
||||
background: rgba(30, 41, 59, 0.85);
|
||||
color: #fff;
|
||||
border-radius: 2px;
|
||||
font-size: 9px;
|
||||
font-weight: 700;
|
||||
letter-spacing: 0.04em;
|
||||
line-height: 1.2;
|
||||
user-select: none;
|
||||
}
|
||||
.zone__popup-summary::-webkit-details-marker { display: none; }
|
||||
.zone__popup-summary::marker { content: ""; }
|
||||
.zone__popup-body {
|
||||
position: absolute;
|
||||
top: 22px;
|
||||
right: 0;
|
||||
width: 360px;
|
||||
max-height: 280px;
|
||||
overflow: auto;
|
||||
padding: 8px 10px;
|
||||
background: #fff;
|
||||
border: 1px solid var(--color-border, #e2e8f0);
|
||||
border-radius: 3px;
|
||||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.12);
|
||||
white-space: pre-wrap;
|
||||
word-break: keep-all;
|
||||
font-size: 10px;
|
||||
line-height: 1.5;
|
||||
color: #1e293b;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
@@ -301,9 +366,19 @@
|
||||
<div class="slide-body">
|
||||
<div class="layout-{{ layout_preset }}">
|
||||
{% for zone in zones %}
|
||||
<div class="zone{% if zone.provisional %} zone--provisional{% endif %}" data-zone-position="{{ zone.position }}" data-template-id="{{ zone.template_id }}"{% if zone.provisional %} data-provisional="1"{% endif %} style="grid-area: {{ zone.position }};">
|
||||
<div class="zone{% if zone.provisional %} zone--provisional{% endif %}" data-zone-position="{{ zone.position }}" data-template-id="{{ zone.template_id }}"{% if zone.provisional %} data-provisional="1"{% endif %}{% if zone.has_popup %} data-has-popup="1"{% endif %} style="grid-area: {{ zone.position }};">
|
||||
{% if zone.provisional %}<span class="zone__needs-adaptation-badge" aria-label="needs user or AI adaptation">needs adaptation</span>{% endif %}
|
||||
{{ zone.partial_html | safe }}
|
||||
{% if zone.has_popup %}
|
||||
{% set _popup_trigger = (zone.popup_binding.detail_trigger if zone.popup_binding else None) or {} %}
|
||||
{% set _popup_placement = _popup_trigger.placement or 'top-right' %}
|
||||
{% set _popup_label = _popup_trigger.label or 'details' %}
|
||||
{% set _popup_strategy = (zone.popup_binding.display_strategy if zone.popup_binding else 'inline_preview_with_details') %}
|
||||
<details class="zone__popup-details zone__popup-details--{{ _popup_placement }}" data-display-strategy="{{ _popup_strategy }}" data-popup-placement="{{ _popup_placement }}">
|
||||
<summary class="zone__popup-summary">{{ _popup_label }}</summary>
|
||||
<div class="zone__popup-body">{{ zone.popup_html }}</div>
|
||||
</details>
|
||||
{% endif %}
|
||||
</div>
|
||||
{% endfor %}
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user