Add Phase Z B4 source-shape-aware placement

- enable B1/B2/B4 source-shape-aware F13 placement behind env flag
- align F13 placement_trace with mapper top_bullets cardinality
- preserve canonical render output when flag is off
This commit is contained in:
2026-05-07 05:26:57 +09:00
parent 8a201337f7
commit 761a43da5e
4 changed files with 151 additions and 23 deletions

View File

@@ -115,26 +115,33 @@ def _assign_region_to_sub_zone(
frame_sub_zones: list[dict[str, Any]],
assigned_sub_zone_ids: set[str],
) -> Optional[dict[str, Any]]:
"""region 에 매칭할 sub_zone 선택 (B4 v0 narrowest-first heuristic).
"""region 에 매칭할 sub_zone 선택.
rule (B4 v0 lock — F29 deadlock 방지) :
Option 1 (source_shape-aware) :
region.source_shape_index 보유 시 *positional 1:1* — frame_sub_zones[index] 채택.
accepts mismatch 또는 already assigned 시 None (rejection).
Legacy (B4 v0 narrowest-first heuristic) :
1. not-yet-assigned 중 region.content_type 을 accepts 하는 후보 수집
2. 후보 중 accepts list 가장 *좁은* sub_zone 우선
3. 동률이면 declaration order (Python sort 의 stability 활용)
예 (F29) :
region.content_type = text_block
candidates = [process_column(accepts=[text,transform], size 2),
product_column(accepts=[text], size 1)]
→ product_column 선택 (narrowest)
region.content_type = transform_table (이후 호출, product_column 이미 assigned)
candidates = [process_column] 만
→ process_column 선택
Returns :
sub_zone dict 또는 None (compatible 후보 없음)
sub_zone dict 또는 None (compatible 후보 없음 / positional accepts mismatch)
"""
# Option 1 positional matching
if region.source_shape_index is not None:
if region.source_shape_index >= len(frame_sub_zones):
return None
sz = frame_sub_zones[region.source_shape_index]
if sz["id"] in assigned_sub_zone_ids:
return None
accepts = sz.get("accepts") or []
if region.content_type not in accepts:
return None
return sz
# Legacy narrowest-first
candidates: list[dict[str, Any]] = []
for sz in frame_sub_zones:
if sz["id"] in assigned_sub_zone_ids: