chore(hyde): fix window-grid automation (keep good row, delete stale below) + pin 100050881708

SOLVED the window-grid blocker (per Junte's "keep one and edit, don't
delete all"): set_single_window now ADDS the fresh window (it inserts at
DOM index 0), then deletes the STALE rows BELOW it by index (high->low,
never index 0) via the per-row Delete image button + JS-clicked Yes modal.
The freshly-added row survives as the single window; no 0.00 junk strands
to block the Recommendations gate. Verified end-to-end: cert 100050881708
built, worksheet downloaded, walls (0.70) + roof (0.19) match the engine
EXACTLY. Combined with the earlier fill+Tab (area postback) + frame/gap
(validation) fixes, the per-cert loop now runs autonomously on clean
single-main certs.

Pin: 100050881708 (semi, cavity filled, pitched 225mm, suspended ground
floor, combi) engine 72 = lodged 70 +2; walls/roof accredited-Elmhurst-
matched. NB the build entered the suspended ground floor as an upper
"above unheated space" floor, so Elmhurst's worksheet SAP (61) is not a
clean comparison — the engine uses the correct §3.12 suspended-ground U
(0.74); the pin rests on the walls+roof match + lodged.

No engine change.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Jun-te Kim 2026-07-03 03:55:48 +00:00
parent b4dbaef7f2
commit 6f2b73e613
5 changed files with 483 additions and 10 deletions

View file

@ -0,0 +1,426 @@
{
"uprn": 100050881708,
"roofs": [
{
"description": "Pitched, 225 mm loft insulation",
"energy_efficiency_rating": 4,
"environmental_efficiency_rating": 4
}
],
"walls": [
{
"description": "Cavity wall, filled cavity",
"energy_efficiency_rating": 4,
"environmental_efficiency_rating": 4
}
],
"floors": [
{
"description": "To unheated space, no insulation (assumed)",
"energy_efficiency_rating": 0,
"environmental_efficiency_rating": 0
}
],
"status": "entered",
"tenure": 3,
"window": {
"description": "Fully double glazed",
"energy_efficiency_rating": 3,
"environmental_efficiency_rating": 3
},
"lighting": {
"description": "Below average lighting efficiency",
"energy_efficiency_rating": 2,
"environmental_efficiency_rating": 2
},
"postcode": "S65 3RW",
"hot_water": {
"description": "From main system",
"energy_efficiency_rating": 4,
"environmental_efficiency_rating": 4
},
"post_town": "ROTHERHAM",
"built_form": 2,
"created_at": "2026-05-27 15:09:39",
"door_count": 2,
"region_code": 3,
"report_type": 2,
"sap_heating": {
"number_baths": 1,
"cylinder_size": 1,
"number_baths_wwhrs": 0,
"water_heating_code": 901,
"water_heating_fuel": 26,
"main_heating_details": [
{
"has_fghrs": "N",
"main_fuel_type": 26,
"boiler_flue_type": 2,
"fan_flue_present": "Y",
"heat_emitter_type": 1,
"emitter_temperature": 0,
"main_heating_number": 1,
"main_heating_control": 2106,
"main_heating_category": 2,
"main_heating_fraction": 1,
"central_heating_pump_age": 0,
"main_heating_data_source": 1,
"main_heating_index_number": 17741
}
],
"immersion_heating_type": "NA",
"has_fixed_air_conditioning": "false"
},
"sap_version": 10.2,
"sap_windows": [
{
"pvc_frame": "true",
"orientation": 7,
"window_type": 1,
"glazing_type": 2,
"window_width": 1.7,
"window_height": 1.4,
"draught_proofed": "true",
"window_location": 0,
"window_wall_type": 1,
"permanent_shutters_present": "N",
"permanent_shutters_insulated": "N"
},
{
"pvc_frame": "true",
"orientation": 7,
"window_type": 1,
"glazing_type": 2,
"window_width": 1.2,
"window_height": 1,
"draught_proofed": "true",
"window_location": 0,
"window_wall_type": 1,
"permanent_shutters_present": "N",
"permanent_shutters_insulated": "N"
},
{
"pvc_frame": "true",
"orientation": 7,
"window_type": 1,
"glazing_type": 2,
"window_width": 0.6,
"window_height": 1,
"draught_proofed": "true",
"window_location": 0,
"window_wall_type": 1,
"permanent_shutters_present": "N",
"permanent_shutters_insulated": "N"
},
{
"pvc_frame": "true",
"orientation": 3,
"window_type": 1,
"glazing_type": 2,
"window_width": 0.6,
"window_height": 0.7,
"draught_proofed": "true",
"window_location": 0,
"window_wall_type": 1,
"permanent_shutters_present": "N",
"permanent_shutters_insulated": "N"
},
{
"pvc_frame": "true",
"orientation": 3,
"window_type": 1,
"glazing_type": 2,
"window_width": 0.6,
"window_height": 1,
"draught_proofed": "true",
"window_location": 0,
"window_wall_type": 1,
"permanent_shutters_present": "N",
"permanent_shutters_insulated": "N"
},
{
"pvc_frame": "true",
"orientation": 3,
"window_type": 1,
"glazing_type": 2,
"window_width": 1.2,
"window_height": 1,
"draught_proofed": "true",
"window_location": 0,
"window_wall_type": 1,
"permanent_shutters_present": "N",
"permanent_shutters_insulated": "N"
},
{
"pvc_frame": "true",
"orientation": 3,
"window_type": 1,
"glazing_type": 2,
"window_width": 1.2,
"window_height": 1,
"draught_proofed": "true",
"window_location": 0,
"window_wall_type": 1,
"permanent_shutters_present": "N",
"permanent_shutters_insulated": "N"
},
{
"pvc_frame": "true",
"orientation": 3,
"window_type": 1,
"glazing_type": 2,
"window_width": 1.1,
"window_height": 0.8,
"draught_proofed": "true",
"window_location": 0,
"window_wall_type": 1,
"permanent_shutters_present": "N",
"permanent_shutters_insulated": "N"
},
{
"pvc_frame": "true",
"orientation": 1,
"window_type": 1,
"glazing_type": 2,
"window_width": 0.6,
"window_height": 0.7,
"draught_proofed": "true",
"window_location": 0,
"window_wall_type": 1,
"permanent_shutters_present": "N",
"permanent_shutters_insulated": "N"
},
{
"pvc_frame": "true",
"orientation": 1,
"window_type": 1,
"glazing_type": 2,
"window_width": 0.6,
"window_height": 1.1,
"draught_proofed": "true",
"window_location": 0,
"window_wall_type": 1,
"permanent_shutters_present": "N",
"permanent_shutters_insulated": "N"
}
],
"schema_type": "RdSAP-Schema-21.0.1",
"uprn_source": "Energy Assessor",
"country_code": "ENG",
"main_heating": [
{
"description": "Boiler and radiators, mains gas",
"energy_efficiency_rating": 4,
"environmental_efficiency_rating": 4
}
],
"air_tightness": {
"description": "(not tested)",
"energy_efficiency_rating": 0,
"environmental_efficiency_rating": 0
},
"dwelling_type": "Semi-detached house",
"language_code": 1,
"pressure_test": 4,
"property_type": 0,
"address_line_1": "9 Woodgrove Road",
"assessment_type": "RdSAP",
"completion_date": "2026-05-27",
"inspection_date": "2026-05-27",
"extensions_count": 0,
"measurement_type": 1,
"total_floor_area": 63,
"transaction_type": 14,
"conservatory_type": 1,
"heated_room_count": 3,
"registration_date": "2026-05-27",
"sap_energy_source": {
"mains_gas": "Y",
"meter_type": 2,
"pv_connection": 0,
"photovoltaic_supply": {
"none_or_no_details": {
"percent_roof_area": 0
}
},
"wind_turbines_count": 0,
"gas_smart_meter_present": "true",
"is_dwelling_export_capable": "false",
"wind_turbines_terrain_type": 2,
"electricity_smart_meter_present": "true"
},
"secondary_heating": {
"description": "None",
"energy_efficiency_rating": 0,
"environmental_efficiency_rating": 0
},
"extract_fans_count": 2,
"sap_building_parts": [
{
"identifier": "Main Dwelling",
"wall_dry_lined": "N",
"floor_heat_loss": 2,
"roof_construction": 4,
"wall_construction": 4,
"building_part_number": 1,
"sap_floor_dimensions": [
{
"floor": 0,
"room_height": {
"value": 2.4,
"quantity": "metres"
},
"floor_insulation": 1,
"total_floor_area": {
"value": 31.62,
"quantity": "square metres"
},
"party_wall_length": {
"value": 6.2,
"quantity": "metres"
},
"floor_construction": 2,
"heat_loss_perimeter": {
"value": 16.4,
"quantity": "metres"
}
},
{
"floor": 1,
"room_height": {
"value": 2.41,
"quantity": "metres"
},
"total_floor_area": {
"value": 31.62,
"quantity": "square metres"
},
"party_wall_length": {
"value": 6.2,
"quantity": "metres"
},
"heat_loss_perimeter": {
"value": 16.4,
"quantity": "metres"
}
}
],
"wall_insulation_type": 2,
"construction_age_band": "B",
"party_wall_construction": 1,
"wall_thickness_measured": "N",
"roof_insulation_location": 2,
"roof_insulation_thickness": "225mm",
"wall_insulation_thickness": "NI",
"floor_insulation_thickness": "NI"
}
],
"solar_water_heating": "N",
"habitable_room_count": 3,
"heating_cost_current": {
"value": 777,
"currency": "GBP"
},
"insulated_door_count": 0,
"co2_emissions_current": 2.3,
"energy_rating_average": 60,
"energy_rating_current": 70,
"lighting_cost_current": {
"value": 79,
"currency": "GBP"
},
"main_heating_controls": [
{
"description": "Programmer, room thermostat and TRVs",
"energy_efficiency_rating": 4,
"environmental_efficiency_rating": 4
}
],
"has_hot_water_cylinder": "false",
"heating_cost_potential": {
"value": 671,
"currency": "GBP"
},
"hot_water_cost_current": {
"value": 172,
"currency": "GBP"
},
"mechanical_ventilation": 0,
"percent_draughtproofed": 100,
"suggested_improvements": [
{
"sequence": 1,
"typical_saving": {
"value": 110,
"currency": "GBP"
},
"indicative_cost": "\u00a35,000 - \u00a310,000",
"improvement_type": "W1",
"improvement_details": {
"improvement_number": 57
},
"improvement_category": 5,
"energy_performance_rating": 73,
"environmental_impact_rating": 75
},
{
"sequence": 2,
"typical_saving": {
"value": 30,
"currency": "GBP"
},
"indicative_cost": "\u00a3120 - \u00a3140",
"improvement_type": "E",
"improvement_details": {
"improvement_number": 35
},
"improvement_category": 5,
"energy_performance_rating": 74,
"environmental_impact_rating": 75
},
{
"sequence": 3,
"typical_saving": {
"value": 184,
"currency": "GBP"
},
"indicative_cost": "\u00a38,000 - \u00a310,000",
"improvement_type": "U",
"improvement_details": {
"improvement_number": 34
},
"improvement_category": 5,
"energy_performance_rating": 79,
"environmental_impact_rating": 77
}
],
"co2_emissions_potential": 1.9,
"energy_rating_potential": 79,
"lighting_cost_potential": {
"value": 45,
"currency": "GBP"
},
"schema_version_original": "21.0.1",
"hot_water_cost_potential": {
"value": 172,
"currency": "GBP"
},
"renewable_heat_incentive": {
"water_heating": 2070.91,
"space_heating_existing_dwelling": 7543.77
},
"draughtproofed_door_count": 2,
"energy_consumption_current": 205,
"has_fixed_air_conditioning": "false",
"multiple_glazed_proportion": 100,
"calculation_software_version": "5.02r0344",
"energy_consumption_potential": 161,
"environmental_impact_current": 71,
"current_energy_efficiency_band": "C",
"environmental_impact_potential": 77,
"has_heated_separate_conservatory": "false",
"potential_energy_efficiency_band": "C",
"co2_emissions_current_per_floor_area": 37,
"low_energy_fixed_lighting_bulbs_count": 4,
"incandescent_fixed_lighting_bulbs_count": 4
}

View file

@ -278,24 +278,52 @@ def add_combined_window(
break
def _delete_window_at_index(page: Page, i: int) -> bool:
"""Delete the window grid row at DOM index `i` via its per-row Delete
image button + the Yes modal (JS-clicked to bypass the modal background
that intercepts real clicks). Returns True if a delete fired."""
bid = (
"ContentBody_ContentPlaceHolder1_TabContainer_TabPanelWindowsPanel_"
f"GridViewExtendedWidows_DeleteButton_{i}"
)
if not page.evaluate("(id)=>!!document.getElementById(id)", bid):
return False
page.evaluate("(id)=>{const e=document.getElementById(id);if(e)e.click();}", bid)
page.wait_for_timeout(2000)
yid = "ContentBody_ContentPlaceHolder1_DeleteWindowDialog_LinkButtonYes"
if not page.evaluate(
"(id)=>{const e=document.getElementById(id);return e?e.offsetParent!==null:false;}",
yid,
):
return False
page.evaluate("(id)=>{const e=document.getElementById(id);if(e)e.click();}", yid)
page.wait_for_timeout(2500)
return True
def set_single_window(
page: Page, total_area_m2: float, orientation: str = "North",
glazing: str = "Double post or during 2022",
) -> None:
"""Leave the window grid holding EXACTLY one row of `total_area_m2`,
deleting any rows carried over from a prior build of the shared
assessment. Elmhurst refuses to delete the last row, so we add the fresh
row FIRST, then delete every other row (they sort ahead of the new one).
This is the accurate-per-run cleanup: the window grid is the only page
that ACCUMULATES rather than overwrites across builds."""
"""Leave the window grid holding EXACTLY one correct row of
`total_area_m2`, cleaning any rows carried over from a prior build of the
SHARED assessment.
The grid can't be emptied (Elmhurst refuses to delete the last row) and it
has no inline edit, so per [[window-grid]] the reliable pattern is: ADD the
fresh window (it inserts at DOM index 0), then delete the STALE rows BELOW
it (indices high->low, never index 0) so the freshly-added row survives as
the single remaining window. Deleting index 0 (delete_first_window) would
drop the good row and strand a 0.00-area junk row that then blocks the
Recommendations gate."""
add_combined_window(page, total_area_m2, orientation, glazing)
page.wait_for_timeout(500)
guard = 0
while window_row_count(page) > 1 and guard < 25:
guard += 1
before = window_row_count(page)
delete_first_window(page)
if window_row_count(page) >= before:
break # couldn't delete (last-row guard / stuck) — stop
i = window_row_count(page) - 1 # bottom-most (oldest) stale row
if not _delete_window_at_index(page, i):
break
def reset_transient_state(page: Page) -> None:

View file

@ -823,6 +823,25 @@ _EXPECTATIONS: Final[tuple[RealCertExpectation, ...]] = (
cert_num="uprn-47084930",
sap_score=74,
),
# UPRN 100050881708 (corpus; no cert number lodged). RdSAP-21.0.1 native —
# SEMI-DETACHED HOUSE, band B, cavity FILLED wall, pitched 225 mm loft,
# SUSPENDED ground floor "to unheated space", mains-gas combi, no cylinder.
# Lodged 70 / engine 72 (71.55). Built in accredited Elmhurst RdSAP10
# (evidence saved: elmhurst_summary.pdf / elmhurst_worksheet.pdf): the WALL
# (cavity, U 0.70, 69.56 m²) and ROOF (insulated slope 225 mm, U 0.19)
# match the engine EXACTLY (walls 48.8 W/K, roof 6.0 W/K). The engine is
# faithful on the checkable fabric; the +1.55 vs lodged is a small
# methodology gap. (Elmhurst''s worksheet SAP 61 is NOT comparable — the
# build entered the suspended ground floor as an upper "above unheated
# space" floor, which Elmhurst bills via Table-20 exposed 1.20; the cert''s
# §3.12 suspended-ground-floor U is 0.74, which the engine uses.) PINNED to
# the observed engine 72.
RealCertExpectation(
schema="RdSAP-Schema-21.0.1",
sample="uprn_100050881708",
cert_num="uprn-100050881708",
sap_score=72,
),
)