fix(condition): match Calico roof covering (element_type "roof")

Verifying against portfolio 824 revealed the Calico condition upload
stores roof covering as the generic `roof` element (renewal on its
`material` aspect), not the enum's pitched/flat_roof_covering variants
(which have zero rows). Add "roof" to the solar-warning roof set so the
warning actually fires against real data; add a regression test and note
the data shape in CONTEXT.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Khalim Conn-Kowlessar 2026-07-14 15:02:28 +00:00
parent ae9ba954eb
commit b0c47432ba
3 changed files with 18 additions and 3 deletions

View file

@ -143,7 +143,7 @@ A dated batch of condition data for one property from a single **source** (e.g.
_Avoid_: survey (unqualified — collides with retrofit/EPC surveys), condition report, upload, batch
**Element**:
One physically distinct part of a home whose condition is surveyed — e.g. roof covering, external wall, boiler. The canonical domain and code term, backed by the `element_type` enum (roof covering is `pitched_roof_covering` / `flat_roof_covering`). An Element belongs to one Condition survey and may recur within it (`element_instance` distinguishes two roofs).
One physically distinct part of a home whose condition is surveyed — e.g. roof covering, external wall, boiler. The canonical domain and code term, backed by the `element_type` enum. (The enum has `pitched_roof_covering` / `flat_roof_covering`, but the Calico data models roof covering as the generic `roof` element, with its renewal on the `material` Aspect — match on the value the data actually uses, not the enum catalogue.) An Element belongs to one Condition survey and may recur within it (`element_instance` distinguishes two roofs).
_Avoid_: component (in code/domain — see **Component**)
**Component**:

View file

@ -26,6 +26,15 @@ describe("evaluateRoofCoveringSolarWarning", () => {
expect(result).toEqual({ renewalYear: 2027, isOverdue: false });
});
it("treats the generic `roof` element as roof covering (Calico data shape)", () => {
const result = evaluateRoofCoveringSolarWarning({
recommendations: [{ type: "solar_pv" }],
elements: [elem({ elementType: "roof", renewalYear: 2018 })],
currentYear: 2026,
});
expect(result).toEqual({ renewalYear: 2018, isOverdue: true });
});
it("uses the earliest renewal across multiple roof-covering elements (pitched + flat)", () => {
const result = evaluateRoofCoveringSolarWarning({
recommendations: [{ type: "solar_pv" }],

View file

@ -1,7 +1,13 @@
import type { ResolvedElement } from "./model";
// Roof covering is an Element; both pitched and flat count for the solar rule.
const ROOF_COVERING_TYPES = new Set(["pitched_roof_covering", "flat_roof_covering"]);
// Roof covering as an Element. The Calico condition data models it as the
// generic `roof` element (renewal carried on its `material` aspect); the enum
// also has pitched/flat variants, included for future data sources.
const ROOF_COVERING_TYPES = new Set([
"roof",
"pitched_roof_covering",
"flat_roof_covering",
]);
export type RoofCoveringSolarWarning = {
renewalYear: number;