Stage 5 commit for IMP-19 (gitea #19) — docs-only, no runtime surface. - new: docs/architecture/IMP-19-ZONE-RATIO-REFERENCE.md (header + A1 consumer + A2 producer + A3 Phase Z solver delta + A4 IMP-09 boundary + A5 re-activation gate / GR1-GR4). - update: PHASE-Z-IMPLEMENTATION-ISSUE-BACKLOG.md — IMP-19 row pending -> documented + reference doc link; IMP-09 row carries soft back-link to the IMP-19 reference doc. - update: PHASE-Q-INSIGHT-TO-22STEP-MAP.md §3 I4 row — prepend IMP-19 anchor + reference doc link (step/classification preserved). Guardrails (Stage 1/2 binding contract): src/ untouched, no role-based ["배경","본심"] hardcoding into Phase Z, IMP-09 solver ownership preserved, soft-link integrity holds. IMP-19 remains dormant until the A5 gate fires. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
10 KiB
IMP-19 — Phase O/Q Zone Ratio Container Pattern Reference
Status: documented (reference-only, dormant)
Scope: doc-only. No runtime surface modified.
Related issue: #19
Soft dependency: IMP-09 (Phase Z Step 8 zone-ratio solver) — IMP-19 stays dormant; activates only via the A5 gate.
Source axis: INSIGHT-MAP §3 / §2.8 I4 — renderer._group_blocks_by_area pattern reference.
A1 — Phase O/Q consumer pattern (read-only reference)
Phase O/Q implements role-based block grouping inside body-side zones at the renderer layer. References (do not modify):
src/renderer.py:210-295—_group_blocks_by_area(blocks, container_specs=None)—OrderedDictgrouping byblock["area"]; whencontainer_specsis supplied andarea ∈ {"body","left","right","hero","detail"}enters the role-container branch (L230).src/renderer.py:234— hardcodedrole_order = ["배경", "본심"]— two-role role-loop axis (block-level container, not zone geometry).src/renderer.py:240-253— topic_id-first match againstspec.topic_ids, then fallback positional fill when topic_id match yields empty (L248-253).src/renderer.py:261-274— inline-style injection:height:{spec.height_px}px; overflow:visible; display:flex; flex-direction:column; gap:8px; font-size:{font_size}px; --spacing-inner:{padding}px; --font-body:{font_size/16}rem;.font_size/paddingare read at:262-263viaspec.block_constraints.get("font_size_px", 15.2)/.get("padding_px", 20)— renderer-side defaults, not producer-emitted (see A2).src/renderer.py:277-279— leftover (unassigned) blocks appended after role containers.src/renderer.py:283-291— non-container branch:len(block_list)==1→ single html, elseflex-direction:columnwrapper withgap:var(--spacing-block); height:100%.
Call sites:
src/renderer.py:352-353—render_multi_page()— passeslayout_concept.get("_container_specs")ascontainer_specsargument (Phase O activation path).src/renderer.py:426—render_slide()— invokes_group_blocks_by_area(blocks_raw)with nocontainer_specs(legacy fallback / unit-test path).
Classification: block/role-level container injection at render time. Not Phase Z zone geometry.
A2 — Phase O upstream producer (read-only reference)
ContainerSpec payloads consumed by A1 are produced upstream. References (do not modify):
src/space_allocator.py:445-586—build_containers_type_b(page_structure, slide_width=1280, slide_height=720, image_sizes=None)— Phase X-B 유형 B container builder.src/space_allocator.py:462-468— token load (_load_design_tokens) +pad,header_h,gap_block,gap_small,inner_wderivation.src/space_allocator.py:470-484— role classification intotop_roles/bottom_roles/footer_rolebyinfo["zone"] ∈ {"top","bottom","bottom_left","bottom_right","footer"}.src/space_allocator.py:486-503— usable height calculation againstslide_body_top=65+slide_body_h=590with optionalfooter_rolecarve-out.src/space_allocator.py:505-510—zone_overhead = zone_count * zone_title_h(28) + (zone_count-1) * zone_gap(16).src/space_allocator.py:512-520—top_h/bottom_hsplit byweightratio overusable_h.src/space_allocator.py:522-537— image-aware top-zone width split (img_w = min(top_h*ratio, inner_w*0.45)).src/space_allocator.py:541-556— top-roleContainerSpecemission:block_constraints = {"img_width_px": img_w, "img_height_px": top_h if img_w>0 else 0, "has_image": img_w>0}— image-aware keys only.src/space_allocator.py:562-574— bottom-roleContainerSpecemission:block_constraints = {}(empty; no producer keys).src/space_allocator.py:577-588— footer-roleContainerSpecemission:block_constraints = {}(empty;max_height_cost="low"literal).
Producer classification: block-level role container with height_px + width_px + block_constraints containing only image-aware keys (img_width_px, img_height_px, has_image) on top role, empty on bottom/footer roles. Not zone-level ratio geometry. font_size_px / padding_px are renderer-side defaults (consumed via .get(..., 15.2) / .get(..., 20) at src/renderer.py:262-263), not producer output.
A3 — Phase Z Step 8 solver delta (IMP-09 owned)
The active Phase Z zone-ratio solver lives in src/phase_z2_pipeline.py and is IMP-09 owned. IMP-19 does not absorb, replace, or amend this surface. References (do not modify):
src/phase_z2_pipeline.py:794-853—compute_zone_layout(zones_data, total_height=SLIDE_BODY_HEIGHT, gap=GRID_GAP)— row-axis solver. Algorithm =min_height_first + content_weight_distribution: Step 1 reserves per-zonemin_height_pxfrom frame_contractvisual_hints(with proportional scale-down on overflow), Step 2 distributes the remaining vertical budget bycontent_weight.score, Step 3 absorbs rounding residual into the last zone. Returnsheights_px+ratios+ reasoning trace.src/phase_z2_pipeline.py:924-972—compute_zone_layout_cols(zones_data, total_width=SLIDE_BODY_WIDTH, gap=GRID_GAP)— col-axis solver. Algorithm =content_weight_distribution_cols(weight-only; nomin_width_pxcontract exists inframe_contracts.yamlper IMP-09 verification). Zero-weight guard splits evenly acrossnzones. Returnswidths_px+width_ratios.src/phase_z2_pipeline.py:1125-1452— topology dispatch surface::1125-1152_build_rows_dynamic—topology=="rows"(horizontal-2): dynamic row heights viacompute_zone_layout, static fr column widths via_parse_fr_string.:1155+_build_grid_dynamic_2d—topology ∈ {T, inverted-T, side-T-left, side-T-right, 2x2}: per-row + per-col virtual-zone aggregation → row solver + col solver →2d_dynamic_aggregatedcomputation.:1444-1452dynamic-branch dispatcher:rows/cols/ 2-D / default fr.:1380-1434user-override geometry branch (computation == "user_override_geometry") — preserves raw override percentages without invoking the weight solver.
Delta vs Phase O/Q (A1+A2):
| Axis | Phase O/Q (renderer._group_blocks_by_area) |
Phase Z Step 8 (compute_zone_layout + cols) |
|---|---|---|
| Geometry level | block/role inside one zone | zone-level row/col tracks across slide_body |
| Width source | role x-anchor + top_h image carve-out |
content_weight share (cols) / fr-string (rows) |
| Height source | producer ContainerSpec.height_px injection |
min_height_first + content_weight remainder |
| Role axis | hardcoded ["배경","본심"] (L234) |
no role concept — zone position + frame contract |
| Min-height source | none (producer-emitted absolute px) | frame_contract visual_hints.min_height_px |
| Topology dispatch | none (single role-loop) | rows / cols / T / inverted-T / side-T-* / 2x2 / single |
| Inline-style injection | yes (height + font_size + spacing-inner) | no (geometry-only; styling handled downstream) |
Conclusion: Phase O role-container pattern and Phase Z zone-ratio solver operate at different abstraction layers (block-in-zone vs zone-in-slide). They are not drop-in interchangeable; IMP-19 surfaces this delta only for design-pattern comparison.
A4 — IMP-09 boundary statement (soft-link)
IMP-19 is soft link: IMP-09. Ownership separation:
- IMP-09 owns: every algorithmic change to
compute_zone_layout,compute_zone_layout_cols, the topology dispatch surface (_build_rows_dynamic/_build_cols_dynamic/_build_grid_dynamic_2d/_build_fr_default), and the frame_contractvisual_hints.min_height_pxcontract. - IMP-19 owns: reference-only documentation of the Phase O/Q
_group_blocks_by_area+build_containers_type_bpattern (A1 + A2) and the Phase Z solver delta narrative (A3). - No bidirectional code flow: IMP-19 does not move Phase O code into Phase Z, and IMP-09 does not consume Phase O
ContainerSpecpayloads. The two solvers remain isolated. - Reference direction is one-way: this document points read-only at
src/renderer.py,src/space_allocator.py, andsrc/phase_z2_pipeline.py. No reverse pointer is required in those source files.
If IMP-09 alters the Phase Z solver signature, A3 must be re-verified (file:line refs); the boundary statement itself does not change.
A5 — Re-activation gate + guardrails
IMP-19 is documented (dormant). Re-activation requires all of the following gate conditions:
- Trigger: Phase Z Step 8 produces a verifiable case where the active solver (
min_height_first + content_weight) yields geometry that the Phase O role-container pattern would have handled correctly — i.e., a regression that maps cleanly to the block-level role abstraction, not the zone-level abstraction. - Evidence requirement: failing-case MDX + frame_contract trace + observed geometry vs expected geometry, attached to a new issue or this issue's reopened state.
- IMP-09 sign-off: the IMP-09 owner confirms the failing case is not addressable inside the Phase Z solver (e.g., adding
visual_hints.min_height_pxor adjustingcontent_weight.scoredoes not resolve it). - Scope re-lock: the new axis is scope-locked under a fresh implementation issue (not silently reopened in IMP-19) so the soft-link contract is preserved.
Guardrails (preserved from Stage 1 + Stage 2):
- GR1 — No runtime integration: this document does not authorize merging Phase O role-container code into the Phase Z runtime. Any such integration requires a new scope-locked issue with its own Stage 1/2 review.
- GR2 — Phase O no-regression: Phase O containers (
render_multi_pagepath with_container_specs) must not re-enter the Phase Z render path; therender_slidelegacy fallback atsrc/renderer.py:426(nocontainer_specs) remains the unit-test entry. - GR3 — Reference extract stays in
docs/architecture/: never undersrc/. No code body copying; file:line refs only. - GR4 — Soft-link integrity: IMP-19 status remains
documenteduntil the A5 gate fires. The IMP-09 backlog entry carries a back-reference (see u3); IMP-19 carries the forward reference here.