Commit graph

1163 commits

Author SHA1 Message Date
KhalimCK
0fdb15ad65
Merge pull request #1645 from Hestia-Homes/fix/terraced-rir-gable-party
Reclassify terraced room-in-roof gables as party, not external
2026-07-20 12:14:08 +01:00
Daniel Roth
dc96cf5aba
Merge pull request #1648 from Hestia-Homes/feature/abri-api-resource-field
Sync third_party_surveyor_identifier deal field from HubSpot
2026-07-20 11:02:11 +01:00
Khalim Conn-Kowlessar
ba90624a02 Re-pin 6035 golden fixture for terraced RR-gable reclassification
6035 is built_form 4 (Mid-Terrace), so this PR routes its Exposed Type-1
RR gable to a party gable (U=0.25) instead of the main-wall U. Heat loss
drops: PE resid -0.14 -> -0.92, CO2 -0.0362 -> -0.0549 (SAP still exact 70).
Residuals stay within noise (PE <0.5% of lodged 191). All 113 golden
fixtures pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-17 14:38:42 +00:00
Daniel Roth
f32628b293 Fix stale comments referencing the removed default-resource fallback
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 13:20:47 +00:00
Daniel Roth
edd4ec00de Make the OpenHousing resource mandatory, dropping the default fallback
The deal's third_party_surveyor_identifier is now required by the log
and amend flows: the trigger contract rejects messages without it
(blank treated as missing, since a blank resource makes OpenHousing
silently drop the appointment), and LogJobRequest/AmendJobRequest carry
it as a required field. The ABRI_RELAY_DEFAULT_RESOURCE fallback and
its config/terraform/workflow wiring are removed as dead code.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 13:13:28 +00:00
Daniel Roth
c8fe4585bf Send the deal's third-party surveyor as the OpenHousing resource
Carry third_party_surveyor_identifier on the Abri trigger message and
through dispatch into the log and amend flows, booking the job against
that resource. LogJobRequest gains the optional resource field
AmendJobRequest already had; the client falls back to the configured
default surveyor when the deal names no third-party resource, so
existing deals behave unchanged.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-17 13:00:47 +00:00
Khalim Conn-Kowlessar
85fe51b623 Reclassify terraced room-in-roof gables as party, not external
A mid-terrace / enclosed-mid-terrace (built_form 4/6) has external walls on
its two opposite (front/rear) faces only (RdSAP 10 §1.1, p.7-8); both end/
gable elevations — and their room-in-roof continuations — abut a neighbour
and are party walls (§1.4.3, p.9). The gov-API path is built_form-blind, so a
cert lodging an "Exposed" RR gable (`gable_wall_type=1`) on such a form had it
billed at the masonry main-wall U (~2.5) instead of the party 0.25 (Table 4,
p.22), over-counting fabric heat loss and under-rating the dwelling.

`_api_type_1_gable_kind` now takes `built_form` and reclassifies an Exposed RR
gable to Party on the both-ends-party forms {4, 6} only; end-terrace {3, 5},
semi {2} and detached {1} keep their genuine exposed gable. The API path lodges
no per-gable U, so this only ever overrides a cascade fallback.

Corroborated three ways: RdSAP §1.1 + Table 4; S10TP-05 Appendix A (a
mid-terrace's roof-gable junction length is zero); and three accredited
Elmhurst mid-terrace worksheets (000477/000480/000516), which all lodge Party
gables. Those worksheets run the site-notes path (already correct) so the 195
Elmhurst pins are untouched.

Corpus (RdSAP-21.0.1, 1000 certs): within-0.5 78.8% -> 79.5%, SAP MAE
0.625 -> 0.599, PE MAE 2.85 -> 2.71, CO2 MAE 0.070 -> 0.068. Cert
100040550095 -10.13 -> +0.37; the 18-cert terraced-external-gable cohort mean
-1.94 -> +0.11; end-terrace/semi/detached untouched. Thresholds ratcheted.

Tests: new test_mapper_rir_gable_terraced (9); 195 Elmhurst pins, 288
cert_to_inputs/real-cert pins, 54 mapper tests, and the corpus gauge all green;
no new pyright errors.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-17 09:32:42 +00:00
Khalim Conn-Kowlessar
eb5f52e9c4 Bill community-heating DHW at the network fuel rate 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-16 18:36:35 +00:00
Khalim Conn-Kowlessar
d301ec3be5 Map Elmhurst 'Positive input from loft' ventilation to natural 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-16 16:26:15 +00:00
KhalimCK
04b7c26f67
PasHub SAP accuracy: infiltration inputs + immersion DHW + lights/lobby/heat-network (#1615)
* Electric-immersion DHW: bill at 100%, not the space boiler's efficiency 🟩

A separate electric immersion heater (SAP water-heating code 903) is 100%
efficient (SAP 10.2 Table 4a) and the space-heating boiler provides no water
heating, so Appendix D2.1 Eq D1 (the boiler seasonal-efficiency cascade) must
not apply to it. But `_water_heating_main` resolves DHW to the SPACE main — a
gas/oil boiler keeps its PCDB record — so three water-efficiency branches in
cert_to_inputs billed the immersion-heated cylinder at the boiler's ~87% summer
efficiency (a mongrel: electric fuel price x gas-boiler efficiency):

  1. the scalar `water_eff = water_pcdb_main.summer_efficiency_pct / 100`,
  2. the SAP §9.4.11 / Table 4c(2) -5pp no-interlock adjustment, and
  3. the Eq D1 (winter, summer) seasonal pair from `pcdb_main`.

Gate all three on `not dhw_is_electric_immersion`; the correct immersion path
(`_water_efficiency_with_category_inherit` -> 1.0, Eq D1 off) then applies,
mirroring the Table 3 zero-primary-loss gate already present for WHC 903.

General bug — fires for any WHC-903 dwelling whose space main is a PCDB gas/oil
boiler. It surfaced via the PasHub campaign's #1600 no-water-heating default
(WHC 999 -> 903) on a gas-combi dwelling: 58 Hackle St M11 4WU, SAP 55.30 ->
57.67 (pre_sap 58, verified 59).

Guardrails: the gov-API RdSAP corpus IMPROVES 78.8% -> 78.9% within-0.5, MAE
0.625 -> 0.622 (a handful of corpus certs with a boiler space main + electric
immersion move closer to accredited) — MAE ceiling ratcheted 0.626 -> 0.625.
Regression pinned in test_cert_to_inputs (RED before / GREEN after). pyright
0-new (cert_to_inputs baseline 30).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* PasHub SAP accuracy: thread low-energy lights + draught lobby (from_site_notes) 🟩

Two systematic silent-drops in the PAS Hub `from_site_notes` path, each a field
the gov-API/Elmhurst mappers thread but site-notes dropped, letting a favourable
default reach the SAP-10.2 calculator. Validated against the correct pashub
oracle (property_baseline_performance.effective_sap_score, portfolio 838):
cohort within-0.5 55.1% → 62.6%, MAE 0.599 → 0.521.

- Low-energy lights: when "exact LED/CFL known = No", PAS Hub lodges an aggregate
  "Number of fixed low energy lights?" count. Previously dropped (no dataclass
  field / extractor key / mapper thread) → calculator saw 0 low-energy bulbs and
  applied the pessimistic L5b/L8c no-data default, under-rating SAP. Now threaded
  into low_energy_fixed_lighting_bulbs_count, mirroring from_elmhurst_site_notes.
  ~6% of the cohort; resolves 58 Hackle (−0.43 vs oracle 59 with #1615).
- Draught lobby: _map_sap_ventilation set only the legacy `draught_lobby` field,
  never the canonical `has_draught_lobby` §2 (13) gate the cascade reads, so the
  surveyed lobby was ignored and infiltration over-stated. Now mirrors Elmhurst.

Two other audited levers were REJECTED against the pashub oracle (they matched
the gov-cert/RdSAP convention but pashub does not apply them): percent_draughtproofed
(cohort 30.8%) and the §A.2.2 assumed secondary heater (19.2%). A heat-network
control-code fix (2306→2303 for 16 Bingley) is HELD pending spec/Elmhurst
adjudication — it contradicts ADR-0053; see the NOTE on _PASHUB_HEAT_NETWORK_CONTROL_TO_SAP10.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* PasHub heat-network control: room-thermostat-only maps to 2308, not 2306 🟩

The Table 4e Group 3 label "charging linked to use of community heating, room
thermostat only" was mapped to 2306 — the linked-to-use *with-TRVs* code
(control type 3, space charging 1.00). The label explicitly excludes TRVs, so
it is control type 2 / space 1.05 = code 2308/2309 (linked-to-use → DHW 1.00).
2306 asserted TRVs the survey doesn't have and over-rated the cohort's sole
heat-network dwelling (16 Bingley Close 56.89 → 54.01 vs pashub oracle 52).

Verified against SAP 10.2 Table 4e as encoded in cert_to_inputs.py
(_CONTROL_TYPE_BY_CODE 2308→2, space-charging 2308→1.05, DHW 2308→1.00) and the
RdSAP control-label vocabulary in MainheatControlAttributes.py. 2303 (the earlier
audit's guess) is rejected: it is a flat-rate code (DHW 1.05) whose oracle match
was a coincidence of two offsetting spec violations. The residual +2.0 to oracle
52 is the documented SAP-10.2-engine-vs-lodged offset, not a fuel/flags gap
(those were threaded by the #1590 follow-up) — supersedes the ADR-0053 /
HANDOVER_838 "community fuel/flags" attribution.

16 Bingley is the only heat-network fixture in portfolio 838, so this moves one
fixture strictly toward its oracle. Harness ratcheted: within-0.5 0.51→0.58,
MAE 0.625→0.521 (observed 58.5% / 0.520 with all three 2026-07-15 levers).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* PasHub SAP accuracy: restore 3 dropped §2 infiltration inputs (from_site_notes)

`from_site_notes` uniquely dropped three ventilation inputs that the gov-API
and Elmhurst sibling mappers all set, causing a systematic cohort-wide SAP
under-rate vs the company's own accredited SAP-10.2 certs
(property_baseline_performance.effective_sap_score, portfolio 838):

1. percent_draughtproofed — never set, so §2(15) window infiltration was
   pinned at its 0.25-ACH worst case on every dwelling. Reconstructed as the
   area-weighted % of draught-proofed windows (mirrors Elmhurst
   `draught_proofing_percent`). Dominant term.
2. Upper-storey +0.25 m joist void — omitted; now added, matching
   `_UPPER_FLOOR_HEIGHT_ADD_M` on the gov-API/Elmhurst paths. This
   RE-ADJUDICATES #1601 ("keep raw"): that call was confounded by the
   then-present draught-proofing drop suppressing every verified dwelling.
3. sheltered_sides — left None → calculator's flat default of 2, which
   over-shelters end/semi/detached (RdSAP §S5 = 1/1/0). Now derived from
   built form.

The three are NON-ADDITIVE — each overshoots alone (which is why all three
were previously rejected individually) — but together they land all 7
verified ground-truth dwellings toward truth (none regress) and every built
form near zero. Cohort 62.1% -> 83.3% within-0.5, MAE 0.507 -> 0.377.

Guardrails: gov-API RdSAP corpus unchanged (78.9%, these are from_site_notes-
only helpers). pashub harness 58.5% -> 79.5% / MAE 0.520 -> 0.389; ratchets
tightened 0.58->0.78 and 0.521->0.40. pyright zero-new (mapper baseline 39).
New focused tests in TestFromSiteNotesInfiltrationFixes cover all three
fields incl. area-weighting and the built-form shelter map; goldens updated.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-16 00:30:55 +01:00
Daniel Roth
a412c8942b Ratchet the API-corpus SAP MAE ceiling to 0.626 for the water-only C6 standing charge 🟪
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 13:50:17 +00:00
Daniel Roth
dd9e316796 Take the full heat-network standing charge, not both, when space heating is also on the network 🟩
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 13:46:08 +00:00
Daniel Roth
b14fa31dde Charge the C6 half heat-network standing charge on a water-only dwelling's off-peak fuel bill 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 13:43:46 +00:00
Daniel Roth
bd3a7ac317 Charge the C6 half heat-network standing charge on a water-only dwelling's fuel cost worksheet 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 13:37:19 +00:00
Daniel Roth
8a96003d45 Include the C6 half heat-network standing charge on top of a water-only dwelling's fuel standing 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 13:31:33 +00:00
Daniel Roth
70d65291c3
Merge pull request #1597 from Hestia-Homes/fix/1592-water-only-heat-network-dhw
Fix the water-only heat-network DHW plant-efficiency double count (#1592 B)
2026-07-15 12:09:30 +01:00
Daniel Roth
a400005353 Ratchet the API-corpus within-0.5 floor to 78.8% for the water-only DHW fix 🟪
Enumerates the 3 corpus certs that can move (all WHC 950) individually per
#1592 sub-task D: two improve (one crossing inside 0.5), one worsens and is
handed to sub-task C for an accredited oracle. No loosening required.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 10:34:19 +00:00
Daniel Roth
97ab27c914 Assert water-only heat-network fuel with the file's tolerance idiom 🟪
pytest.approx is untyped under pyright strict, so the new assertions added
3 errors against the repo's zero-new-errors bar. Switches to the abs(...)
<= tol form already dominant in this file. No behaviour change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 10:33:10 +00:00
Daniel Roth
15b78ba312 Treat water-only heat-network primary pipework as insulated 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 10:29:12 +00:00
Daniel Roth
d10b5210c6 Accrue Table 3 primary-circuit loss for water-only heat-network cylinders 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 10:27:54 +00:00
Daniel Roth
5f3e9f893f Bill water-only heat-network DHW identically whatever plant is behind it 🟩
950 boilers / 951 CHP / 952 community heat pump differ only in plant
efficiency, which SAP 10.2 §4.3 puts in the Table 12 heat price — so the
dwelling's delivered-heat demand must not vary with it. Passes on the
previous commit's expression; verified RED against the prior impl, where
951 leaked 0.80/0.75 and 952 under-billed its DHW fuel by 3.15×.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 10:25:59 +00:00
Daniel Roth
353ebe5556 Price water-only heat-network DHW without a plant-efficiency divisor 🟩
SAP 10.2 §4.3 (p.24) puts the water-heating efficiency in the Table 12
heat price, and C4 (p.53) scopes the plant efficiency to CO2/PE only, so
worksheet (310) = (64) × (305a) × (306) carries no efficiency term. The
dwelling-side divisor was applying it a second time.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 10:24:34 +00:00
Daniel Roth
bf4af89b3a Price water-only heat-network DHW without a plant-efficiency divisor 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 10:22:37 +00:00
Daniel Roth
870ec68dba Model the newer stored survey when refetching EPCs 🟩
Read stored lodged EPCs whatever refetch_epc says, so a stored assessment
can win the ADR-0001 Recency Tie-Break against the gov register.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 09:40:15 +00:00
Daniel Roth
7a2f29027c Model the newer stored survey when refetching EPCs 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 09:37:35 +00:00
Daniel Roth
04f72e2d32 Keep the stored survey when the register has no cert or ties on date 🟩
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 09:36:21 +00:00
Daniel Roth
3a76bd0f7c Use the fetched gov cert when nothing is stored 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 09:35:01 +00:00
Daniel Roth
22bd0eadbe Use the fetched gov cert when it is the newer assessment 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 09:33:28 +00:00
Daniel Roth
d1be7c2842 Prefer the newer of a stored survey and a fetched gov cert 🟥
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 09:32:50 +00:00
Daniel Roth
be076bb9a4 Resolve the cohort's remaining PasHub boiler control labels to Table 4e codes 🟩
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 16:22:23 +00:00
Jun-te Kim
3dd86f296d
Merge pull request #1549 from Hestia-Homes/feature/expired-predicted-source
feat(modelling_e2e): condition prediction on the expired Historic EPC (ADR-0054)
2026-07-14 13:14:05 +01:00
Jun-te Kim
efdb8e816c feat(modelling_e2e): condition prediction on the expired Historic EPC (ADR-0054)
ADR-0054 gives a prediction conditioned by an expired Historic EPC the source
"expired", so reporting can tell "no EPC at all" apart from "only an expired
one". It was implemented in IngestionOrchestrator — but the pipeline that
actually runs is modelling_e2e, which predicts through its own _predict_epc and
never adopted it. Result: zero `expired` rows have ever been written, and
`epc_property.source` holds only 'lodged' and 'predicted'.

Two things kept the flavour unreachable here, and both had to go:

  - _predict_epc never looked at the historic backup at all, so no prediction
    was ever conditioned.
  - _flush_writes passed the literal source="predicted", and _PropertyWrite had
    no field to carry a flavour, so one would have been dropped before the write
    even if computed.

_predict_epc now mirrors IngestionOrchestrator._predict: the expired cert's
stable attributes fill the gaps Landlord Overrides left (overrides still win
where both speak) and condition the cohort, and it returns the source alongside
the EPC. _PropertyWrite carries it; _flush_writes persists it. save_batch
already groups deletes by source family, so a mixed predicted/expired batch
clears the shared slot correctly.

Ships dark: the reader is built only when HISTORIC_EPC_S3_ROOT is set, so
without it every prediction stays plain "predicted", exactly as today.
DEFAULT_S3_ROOT names the dev bucket, so defaulting to it would have a prod
lambda silently reading dev data — Terraform sets the var per environment.

Note this also rescues properties that currently fail to model outright: an
expired cert can supply the property_type no override resolved, where today
that raises UnresolvedPropertyTypeError.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 11:15:39 +00:00
Daniel Roth
c9cb59ea78 Break lodged-UPRN duplicates by latest inspection_date, then id
inspection_date is non-null on epc_property, so it is a better winner for
a shared-UPRN collision than insertion order: the latest-surveyed lodged
row wins. id DESC stays as the deterministic secondary, so a same-UPRN,
same-inspection_date tie falls to the highest id.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 10:46:01 +00:00
Daniel Roth
f7328f54c5 Fetch a property's lodged EPC by UPRN, not property_id
An epc_property row can carry a null property_id (FE/historic ingestion
persists EPC rows never linked to a property row), so a property_id-keyed
read silently misses them. UPRN is the durable key both the property row
and the epc_property row share. Repoint the lodged reads (get_for_property
+ get_for_properties) onto UPRN; the predicted reads stay on property_id
because a predicted EPC deep-copies a neighbour's UPRN, so its UPRN column
is never the property's own.

UPRN is not unique on epc_property, so the read tie-break is now load-
bearing (it was dormant under property_id, where the write path guarantees
one lodged row per property): the most recently ingested (highest-id) row
wins. Write path stays keyed on (property_id, source).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 10:35:51 +00:00
Khalim Conn-Kowlessar
3bbe293f68 Gate the loft top-up strictly below the 270mm building-regs depth 🟩
Locks the gate edges — a lodged pitched loft below 270mm is recommended, a
loft at 270mm is left alone, and the 'Nmm+' form ('300mm+') parses to its
number and stays ineligible. These passed on arrival (they fell out of the
numeric-parse gate added in the previous commit); pinned as regression guards.
ADR-0063.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 16:07:48 +00:00
Khalim Conn-Kowlessar
2fe5b79dc4 Recommend loft insulation for a loft below the building-regs depth 🟥
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 16:03:39 +00:00
Jun-te Kim
64abeb9066 Map SAP-Schema-15.0's "Main building" identifier to MAIN
Properties 741843/741872 (uprns 100060714155/100021944594) failed
modelling_e2e subtasks 6ac7841a-9338-4efe-97e5-7e0d19b3055d and
c5450f03-5b0d-4068-a3b3-d1470bc0af57 with a bare StopIteration: their
SAP-Schema-15.0 (2011-era LIG-lodged) certs identify the main dwelling's
building part as "Main building" rather than "Main Dwelling", so
from_api_string fell to OTHER and left the property with no MAIN part —
crashing wall_recommendation.py's unguarded next(...).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-10 15:03:28 +00:00
KhalimCK
660e42b920
Merge pull request #1526 from Hestia-Homes/feature/fabric-first-scenario
Fabric-first scenario constraint: two-phase optimisation (ADR-0061)
2026-07-10 12:48:06 +01:00
Jun-te Kim
121b3d991d
Merge pull request #1533 from Hestia-Homes/fix/epc-schema-allowlist
Unsupported EPC schema versions skip with a warning instead of crashing
2026-07-10 12:43:40 +01:00
Daniel Roth
d72f800be8
Merge pull request #1534 from Hestia-Homes/feature/abri-api-integration
Abri: always send a bookable resource on log/amend (+ survey-time labels)
2026-07-10 12:39:53 +01:00
Khalim Conn-Kowlessar
7996eedf55 Goal-objective test uses the shared fixtures and a real boiler code 🟪
Review findings on PR #1527:

- The overlay constants, ScoredOption builder, ventilation dependency and
  selected_types helper come from the shared _optimiser_fixtures module
  (landed on the fabric-first base) instead of local copies; the boiler
  overlay is the shared BOILER_OVERLAY (SAP Table 4a code 104, a mains-gas
  combi) rather than code 201, which is neither a boiler nor a heat pump.
  _IWI_OVERLAY (solid-wall internal, type 3) stays local — no shared
  equivalent — and the carbon stubs stay bespoke (the shared StubScorer has
  no CO2 knob).
- The optimise_package_fabric_first import is lifted to module scope.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 11:17:27 +00:00
Jun-te Kim
b540cf1ca2 Unsupported EPC schema versions are skipped with a warning, not a crash
An unrecognised schema_type used to raise ValueError and abort the whole
call; now from_api_response logs a warning and returns None so one
unmapped cert doesn't break a batch. Schema 15.0 already has a mapper
(PR #1531) so it's unaffected; only genuinely unmapped versions skip.
2026-07-10 11:17:07 +00:00
Khalim Conn-Kowlessar
c70f6730a3 Remove the superseded role-1 impacts scorer; signals carry the objective currency 🟪
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 11:11:07 +00:00
Khalim Conn-Kowlessar
ffaf89935b Fabric-first phase 2 re-scores candidates in the goal objective's currency 🟥
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 11:10:06 +00:00
Khalim Conn-Kowlessar
102b250e53 A goal-aligned scenario without a budget fails loudly 🟥
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 11:10:06 +00:00
Khalim Conn-Kowlessar
6b64104dc5 An Energy-Savings scenario prices packages at the live fuel rates 🟥
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 11:10:06 +00:00
Khalim Conn-Kowlessar
aac35327f7 A Reducing-CO2 scenario maximises carbon reduction, not SAP 🟥
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 11:10:06 +00:00
Khalim Conn-Kowlessar
a8e2d99018 Dependency signals are priced in the goal objective's currency 🟥
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-10 11:10:05 +00:00
Daniel Roth
208113ae6d send AM or PM instead of trying to parse morning or afternoon 2026-07-10 10:45:57 +00:00
Daniel Roth
f8b9d82200 Always send a bookable resource on Abri amend_job
OpenHousing logs a job but silently drops the appointment when no bookable
resource is sent, so the survey date never lands. log_job already sends the
configured default_resource; amend_job omitted the parameter unless the
request carried an explicit surveyor. Default amend's resource to the
configured surveyor too, and fail loudly at config load on a blank
ABRI_RELAY_DEFAULT_RESOURCE so the misconfiguration can't ship an empty
resource again.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-10 10:37:35 +00:00