From 6fc10683d29d725c56fd450ca6c8df7a1db1dac3 Mon Sep 17 00:00:00 2001 From: Khalim Conn-Kowlessar Date: Wed, 1 Jul 2026 09:35:24 +0000 Subject: [PATCH] =?UTF-8?q?Resolve=20a=20party-ceiling=20roof=20marker=20t?= =?UTF-8?q?o=20the=20party-ceiling=20member=20=F0=9F=9F=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 4.8 (1M context) --- .../roof_party_ceiling_guard.py | 23 +++++++++++++++++++ .../epc/test_roof_party_ceiling_guard.py | 18 +++++++++++++++ 2 files changed, 41 insertions(+) create mode 100644 domain/epc/property_overrides/roof_party_ceiling_guard.py create mode 100644 tests/domain/epc/test_roof_party_ceiling_guard.py diff --git a/domain/epc/property_overrides/roof_party_ceiling_guard.py b/domain/epc/property_overrides/roof_party_ceiling_guard.py new file mode 100644 index 000000000..97cdb09b1 --- /dev/null +++ b/domain/epc/property_overrides/roof_party_ceiling_guard.py @@ -0,0 +1,23 @@ +from __future__ import annotations + +from typing import Optional + +from domain.epc.property_overrides.roof_type import RoofType + + +def roof_party_ceiling_guard(description: str) -> Optional[RoofType]: + """Deterministically resolve a party-ceiling roof description to its RoofType. + + A building part whose top boundary is another (or the same) dwelling / premises + above has ~0 heat loss (RdSAP 10 Table 18: "There is no heat loss through the + roof of a building part that has the same dwelling or another dwelling above"), + so it must resolve to a party-ceiling `RoofType` member — which the roof + Simulation Overlay leaves as no overlay, keeping the lodged EPC — and never to + an external `Pitched` / `Flat` roof. + + Recognises the party-ceiling markers regardless of a trailing insulation token + (``: 100mm`` / ``: unknown``) or spacing/case, and returns ``None`` for anything + that is not a party-ceiling marker so the LLM classifier still handles it + (#1376). + """ + raise NotImplementedError diff --git a/tests/domain/epc/test_roof_party_ceiling_guard.py b/tests/domain/epc/test_roof_party_ceiling_guard.py new file mode 100644 index 000000000..3c155f23c --- /dev/null +++ b/tests/domain/epc/test_roof_party_ceiling_guard.py @@ -0,0 +1,18 @@ +from __future__ import annotations + +from domain.epc.property_overrides.roof_type import RoofType +from domain.epc.property_overrides.roof_party_ceiling_guard import ( + roof_party_ceiling_guard, +) + + +def test_party_ceiling_marker_with_a_trailing_depth_resolves_to_the_party_ceiling_member() -> None: + # Arrange — the bug: a party ceiling ("another dwelling above") carrying a + # trailing loft depth the LLM misreads as an external pitched roof. + description = "another dwelling above: 100mm" + + # Act + result = roof_party_ceiling_guard(description) + + # Assert — it is a party ceiling (~0 heat loss), not a Pitched roof. + assert result is RoofType.ADJACENT_ANOTHER_DWELLING_ABOVE