feat(layout): reduce default MAM_MIN_PANE_COLS to 40 for single workspace 2xK multi-pane tiling

This commit is contained in:
2026-08-24 15:06:02 +09:00
parent 7ce71c96b1
commit b72412be95
8 changed files with 508 additions and 9 deletions
+143 -2
View File
@@ -287,7 +287,7 @@ split_dir=""
# Exact snippet from lib.sh:429-435
if [ -n "$sample_pane" ]; then
layout_raw=$(_real_herdr pane layout --pane "$sample_pane" 2>/dev/null || echo "")
read -r split_dir split_target < <(printf '%s' "$layout_raw" | python3 -m lib_py.layout --min-cols "${{MAM_MIN_PANE_COLS:-60}}" --min-rows "${{MAM_MIN_PANE_ROWS:-20}}" --sample-pane "$sample_pane" 2>/dev/null || echo "right $sample_pane")
read -r split_dir split_target < <(printf '%s' "$layout_raw" | python3 -m lib_py.layout --min-cols "${{MAM_MIN_PANE_COLS:-40}}" --min-rows "${{MAM_MIN_PANE_ROWS:-20}}" --sample-pane "$sample_pane" 2>/dev/null || echo "right $sample_pane")
split_dir="${{split_dir:-right}}"
sample_pane="${{split_target:-$sample_pane}}"
fi
@@ -435,7 +435,7 @@ _ZERO_TRAP = {"result": {"panes": [
def test_j1_env_zero_min_cols_matches_flag_zero():
"""J-1: MAM_MIN_PANE_COLS=0 must mean 0, not fall through to the 60 default."""
"""J-1: MAM_MIN_PANE_COLS=0 must mean 0, not fall through to the 40 default."""
flag = _run_layout(_ZERO_TRAP, ("--min-cols", "0"))
assert flag["direction"] == "right" and flag["reason"] == "single_pane_height_constrained"
for var in ("MAM_MIN_COLS", "MAM_MIN_PANE_COLS"):
@@ -479,3 +479,144 @@ def test_j1b_invalid_alias_does_not_shadow_the_documented_var():
"MAM_MIN_PANE_COLS": "bar"}) == _run_layout(_ZERO_TRAP)
def test_default_min_cols_is_40():
"""Verify compute_2xk_layout default min_cols is 40.
With width 80 (width//2 = 40):
- min_cols=40 -> 40 >= 40 -> split right (new column).
- min_cols=60 -> 40 < 60 -> overflow.
Default invocation (no min_cols passed) must split right.
"""
payload = {
"result": {
"panes": [
{"pane_id": "p1", "rect": {"x": 0, "y": 0, "width": 80, "height": 40}},
{"pane_id": "p2", "rect": {"x": 0, "y": 40, "width": 80, "height": 40}},
]
}
}
decision = compute_2xk_layout(payload)
assert decision.direction == "right"
assert not decision.is_overflow
assert decision.reason == "new_column_right"
def test_80_col_2_column_splitting_boundary():
"""Verify width >= 80 cols allows 2-column splitting with default min_cols=40,
while width < 80 (e.g. 79) triggers column_width_overflow.
"""
# 80 cols: 80 // 2 = 40 == min_cols(40) -> splits right
payload_80 = {
"result": {
"panes": [
{"pane_id": "p1", "rect": {"x": 0, "y": 0, "width": 80, "height": 40}},
{"pane_id": "p2", "rect": {"x": 0, "y": 40, "width": 80, "height": 40}},
]
}
}
d80 = compute_2xk_layout(payload_80)
assert d80.direction == "right"
assert not d80.is_overflow
# 79 cols: 79 // 2 = 39 < min_cols(40) -> overflow
payload_79 = {
"result": {
"panes": [
{"pane_id": "p1", "rect": {"x": 0, "y": 0, "width": 79, "height": 40}},
{"pane_id": "p2", "rect": {"x": 0, "y": 40, "width": 79, "height": 40}},
]
}
}
d79 = compute_2xk_layout(payload_79)
assert d79.direction == "overflow"
assert d79.is_overflow
assert d79.reason == "column_width_overflow"
def test_90_col_single_workspace_multi_pane_tiling():
"""Verify complete 1 -> 2 -> 3 -> 4 pane tiling in a 90-col single workspace.
- 1 pane (90x40): splits down to p1(90x20), p2(90x20)
- 2 panes: splits right to start col 2 -> p3(45x40)
- 3 panes: fills singleton col 2 down -> p4(45x20)
- 4 panes (2x2 grid): 5th agent overflows because 45 // 2 = 22 < 40
"""
# 1 -> 2
p1_layout = {"result": {"panes": [{"pane_id": "p1", "rect": {"x": 0, "y": 0, "width": 90, "height": 40}}]}}
d1 = compute_2xk_layout(p1_layout)
assert d1.direction == "down"
assert d1.target_pane_id == "p1"
# 2 -> 3
p2_layout = {"result": {"panes": [
{"pane_id": "p1", "rect": {"x": 0, "y": 0, "width": 90, "height": 20}},
{"pane_id": "p2", "rect": {"x": 0, "y": 20, "width": 90, "height": 20}},
]}}
d2 = compute_2xk_layout(p2_layout)
assert d2.direction == "right"
assert d2.target_pane_id == "p1"
assert not d2.is_overflow
# 3 -> 4
p3_layout = {"result": {"panes": [
{"pane_id": "p1", "rect": {"x": 0, "y": 0, "width": 45, "height": 20}},
{"pane_id": "p2", "rect": {"x": 0, "y": 20, "width": 45, "height": 20}},
{"pane_id": "p3", "rect": {"x": 45, "y": 0, "width": 45, "height": 40}},
]}}
d3 = compute_2xk_layout(p3_layout)
assert d3.direction == "down"
assert d3.target_pane_id == "p3"
assert not d3.is_overflow
# 4 -> 5 (overflow to new workspace)
p4_layout = {"result": {"panes": [
{"pane_id": "p1", "rect": {"x": 0, "y": 0, "width": 45, "height": 20}},
{"pane_id": "p2", "rect": {"x": 0, "y": 20, "width": 45, "height": 20}},
{"pane_id": "p3", "rect": {"x": 45, "y": 0, "width": 45, "height": 20}},
{"pane_id": "p4", "rect": {"x": 45, "y": 20, "width": 45, "height": 20}},
]}}
d4 = compute_2xk_layout(p4_layout)
assert d4.direction == "overflow"
assert d4.is_overflow
assert d4.reason == "column_width_overflow"
def test_100_col_single_workspace_multi_pane_tiling():
"""Verify complete 1 -> 2 -> 3 -> 4 pane tiling in a 100-col single workspace."""
# 1 -> 2
p1_layout = {"result": {"panes": [{"pane_id": "p1", "rect": {"x": 0, "y": 0, "width": 100, "height": 40}}]}}
d1 = compute_2xk_layout(p1_layout)
assert d1.direction == "down"
# 2 -> 3
p2_layout = {"result": {"panes": [
{"pane_id": "p1", "rect": {"x": 0, "y": 0, "width": 100, "height": 20}},
{"pane_id": "p2", "rect": {"x": 0, "y": 20, "width": 100, "height": 20}},
]}}
d2 = compute_2xk_layout(p2_layout)
assert d2.direction == "right"
assert not d2.is_overflow
# 3 -> 4
p3_layout = {"result": {"panes": [
{"pane_id": "p1", "rect": {"x": 0, "y": 0, "width": 50, "height": 20}},
{"pane_id": "p2", "rect": {"x": 0, "y": 20, "width": 50, "height": 20}},
{"pane_id": "p3", "rect": {"x": 50, "y": 0, "width": 50, "height": 40}},
]}}
d3 = compute_2xk_layout(p3_layout)
assert d3.direction == "down"
assert d3.target_pane_id == "p3"
assert not d3.is_overflow
# 4 -> 5 (overflow to new workspace)
p4_layout = {"result": {"panes": [
{"pane_id": "p1", "rect": {"x": 0, "y": 0, "width": 50, "height": 20}},
{"pane_id": "p2", "rect": {"x": 0, "y": 20, "width": 50, "height": 20}},
{"pane_id": "p3", "rect": {"x": 50, "y": 0, "width": 50, "height": 20}},
{"pane_id": "p4", "rect": {"x": 50, "y": 20, "width": 50, "height": 20}},
]}}
d4 = compute_2xk_layout(p4_layout)
assert d4.direction == "overflow"
assert d4.is_overflow
assert d4.reason == "column_width_overflow"