Sub-Ladder rungs never take from north planes 🟩

Pins behavior delivered with the tracer slice (derivation starts from the
north-dropped remainder) — no red phase; the mutation is discriminating
(a kept north panel would win fill-by-generation and shift the yields).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Khalim Conn-Kowlessar 2026-07-07 22:07:18 +00:00
parent c1a8c27bc1
commit 5a382948d5

View file

@ -304,3 +304,47 @@ def test_sub_ladder_rungs_fill_from_the_highest_yield_segment_first() -> None:
(225.0, 2),
(135.0, 1),
}
def test_sub_ladder_rungs_never_take_from_north_planes() -> None:
# ADR-0058 — derivation starts from the north-dropped remainder: a north
# plane never contributes panels to a Sub-Ladder rung, even when its
# per-panel yield would win the fill-by-generation ordering.
# Arrange — smallest rung = a due-north panel (900/panel, best yield) plus
# a south plane (4 panels, 1278); max 5 → cap 3.5; after the north drop
# the 4-panel south remainder still exceeds the cap → Sub-Ladder fires.
north = SolarRoofSegment(
segment_index=0,
panels_count=1,
azimuth_degrees=2.0,
pitch_degrees=30.0,
yearly_energy_dc_kwh=900.0,
)
south = SolarRoofSegment(
segment_index=1,
panels_count=4,
azimuth_degrees=180.0,
pitch_degrees=30.0,
yearly_energy_dc_kwh=1278.0,
)
potential = SolarPotential(
panel_capacity_watts=400.0,
max_array_panels_count=5,
configurations=(
SolarPanelConfiguration(
panels_count=5,
yearly_energy_dc_kwh=2178.0,
segments=(north, south),
),
),
)
# Act
configs = select_conservative_configs(potential)
# Assert — rungs 2 and 3 built from the south plane alone, pro-rata off
# its 1278 (639 / 958.5); the 900-yield north panel never appears.
assert [c.panels_count for c in configs] == [2, 3]
assert [c.yearly_energy_dc_kwh for c in configs] == [639.0, 958.5]
assert all(s.azimuth_degrees == 180.0 for c in configs for s in c.segments)