Route sloping-ceiling roof descriptions deterministically off the loft ladder 🟩

Compose the party-ceiling and sloping-ceiling guards in the roof
GuardedColumnClassifier so a PitchedWithSlopingCeiling override resolves to the
Pitched, sloping ceiling family instead of being routed onto Pitched, N mm loft
insulation by the LLM (ADR-0066).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Khalim Conn-Kowlessar 2026-07-23 13:53:56 +00:00
parent 82ac8c341b
commit 6f8724aa04
2 changed files with 24 additions and 9 deletions

View file

@ -18,9 +18,7 @@ from domain.epc.property_overrides.main_heating_guard import main_heating_guard
from domain.epc.property_overrides.property_type import PropertyType
from domain.epc.property_overrides.property_type_guard import property_type_guard
from domain.epc.property_overrides.roof_type import RoofType
from domain.epc.property_overrides.roof_party_ceiling_guard import (
roof_party_ceiling_guard,
)
from domain.epc.property_overrides.roof_guard import roof_guard
from domain.data_transformation.guarded_column_classifier import (
GuardedColumnClassifier,
)
@ -133,12 +131,13 @@ def _build_columns(
"roof_type": lambda src: ClassifiableColumn(
name="roof_type",
source_column=src,
# A party ceiling ("another/same dwelling or premises above") has ~0
# heat loss and must never be classified as an external roof; the
# deterministic guard resolves those markers and the LLM handles the
# rest (#1376).
# Deterministic roof guards resolve the markers they are certain of
# ahead of the LLM: a party ceiling ("another/same dwelling or
# premises above", ~0 heat loss, #1376) and a slope-following ceiling
# ("PitchedWithSlopingCeiling", rafter/slope-line insulation, never
# the loft ladder, ADR-0066). The LLM handles the rest.
classifier=GuardedColumnClassifier(
guard=roof_party_ceiling_guard,
guard=roof_guard,
fallback=ChatGptColumnClassifier(chat_gpt, RoofType, RoofType.UNKNOWN),
),
repo=LandlordOverridesRepository[RoofType](

View file

@ -2,8 +2,24 @@ from __future__ import annotations
from typing import Optional
from domain.epc.property_overrides.roof_party_ceiling_guard import (
roof_party_ceiling_guard,
)
from domain.epc.property_overrides.roof_sloping_ceiling_guard import (
roof_sloping_ceiling_guard,
)
from domain.epc.property_overrides.roof_type import RoofType
def roof_guard(description: str) -> Optional[RoofType]:
raise NotImplementedError
"""The deterministic roof guard for the roof `GuardedColumnClassifier`.
Composes the two roof guards that resolve a description with certainty, ahead
of the LLM fallback: a party ceiling (~0 heat loss, #1376) and a
slope-following ceiling (rafter/slope-line insulation, ADR-0066). The two
marker sets are disjoint, so precedence is immaterial; anything neither
recognises returns None and reaches the LLM classifier.
"""
return roof_party_ceiling_guard(description) or roof_sloping_ceiling_guard(
description
)