Commit graph

1646 commits

Author SHA1 Message Date
Khalim Conn-Kowlessar
463e17debc Merge remote-tracking branch 'origin/main' into feature/add-properties-by-postcode 2026-07-06 14:04:30 +00:00
KhalimCK
03a427193f
Merge pull request #354 from Hestia-Homes/chore/override-source-os-places
feat(db): add os_places to override_source enum
2026-07-06 14:52:24 +01:00
Khalim Conn-Kowlessar
c8c00b87b5 feat(db): add os_places to override_source enum
New provenance value for the deterministic OS Places classification-code
mapping written by the postcode-search journey (ADR-0007, on
feature/add-properties-by-postcode). Enum-only change so it can be
applied to the database ahead of the feature merging.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 13:46:33 +00:00
Khalim Conn-Kowlessar
302e200ed6 docs: handover for postcode-search build continuation
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 13:38:54 +00:00
Khalim Conn-Kowlessar
5b224677dc feat(postcode-search): pure domain core — normalisation, OS-code mapping, write plan
TDD'd (9 tests): postcode normalisation to canonical form; the ADR-0007
deterministic OS classification mapping (RD02/03/04/06/07; other
residential codes selectable with no facts, Unknown never written;
RD10 + non-RD unselectable); 30-day cache freshness for postcode_search;
DPA-preferred UPRN-deduped address candidates with postcode-stripped
title-case addresses; buildWritePlan producing the property row
(incl. local_authority/constituency from postcodes.io — closing the
no-source gap) and 0-2 property_overrides facts keyed to the OS code.

Adds 'os_places' to OverrideSourceValues — enum migration still needs
generating via generate_migration.sh before deploy.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 13:32:18 +00:00
Khalim Conn-Kowlessar
b86efddfdf chore(adr): stop gitignoring docs/adr; add ADR-0007 for postcode-search property creation
The docs/adr/** ignore rule silently ate ADRs 0004-0006 that CONTEXT.md
still references. Remove it so decisions actually land in the repo.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 13:07:43 +00:00
Daniel Roth
b94feae398
Merge pull request #352 from Hestia-Homes/hs-number_of_attempts
Add numberOfAttempts to hubspot deals table
2026-07-03 12:16:59 +01:00
Daniel Roth
cd8a43c798 migration files 2026-07-03 10:38:25 +00:00
Daniel Roth
a6c642966e add number of attempts to hubspot deals table 2026-07-03 10:38:09 +00:00
Jun-te Kim
f788c858c6
Merge pull request #350 from Hestia-Homes/fix/plan-recommendations-rec-id-index
Add index on plan_recommendations.recommendation_id to Drizzle schema
2026-07-02 17:37:49 +01:00
Jun-te Kim
8e39c00ec8 Add journal-tracked migration 0258 for the recommendation_id index
Generated with drizzle-kit generate, then hand-edited to IF NOT EXISTS:
the index already exists on the live DB (created CONCURRENTLY on
2026-07-02), so the migration must no-op there while still building the
index in fresh environments. Snapshot + journal are updated so the next
generated migration doesn't re-emit this index.

Applying it (drizzle-kit migrate) remains a human action.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 16:30:42 +00:00
KhalimCK
96245a05af
Merge pull request #349 from Hestia-Homes/fix/reporting-carbon-effective-baseline
fix(reporting): negative scenario carbon impact — move carbon fragments to effective baseline
2026-07-02 17:29:08 +01:00
Jun-te Kim
b49fbc4265 Add index on plan_recommendations.recommendation_id to Drizzle schema
Plan deletes cascade to recommendation rows, and each cascaded delete
fires an FK check against plan_recommendations.recommendation_id. With
~26M rows and no index on that column the check seq-scans per row,
making any plan deletion path time out.

The index already exists on the live DB (created CONCURRENTLY on
2026-07-02 as idx_plan_recommendations_recommendation_id, 74s build);
this brings the Drizzle schema in line so it stops drifting. No
migration is included — generating/applying it is left as a human
action, and it must reuse the existing index name (IF NOT EXISTS) so
it no-ops against prod.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 16:24:57 +00:00
Khalim Conn-Kowlessar
1cd48bc78e fix(reporting): negative scenario carbon impact — move carbon fragments to effective baseline
"Total carbon saved" on the reporting scenario overlay went negative
(portfolio 796: -10,673 t/yr, £/tCO2 -8,195). Cause: the baseline side
(getTotals → carbonSql) summed bp.lodged_co2_emissions_t_per_yr, which
is NULL for predicted-only properties (12,064 of 796's 31,919 — no
mirrored lodged estimate is written), so they contributed nothing to
the baseline. The scenario side counted their plans' post_co2_emissions
(21,046 t) in full, dragging saved below zero. The same subtraction fed
£-per-tonne-CO2 and average-per-unit.

carbonSql and energyConsumptionSql were left on lodged_* when ADR-0002
(effective performance is the canonical current, superseding "reporting
stays on lodged") migrated the SAP fragments — the same reporting cards
were already mixing effective SAP with lodged carbon. Move both to
effective_*, which is populated for every property with a baseline row
and is the figure the plans' post_* values were scored against. All
call sites (reporting averages/totals, both scenario metrics routes,
portfolio table CO2 column/filter) are current-state surfaces, so none
wanted lodged. Legacy branch untouched.

Verified live on 796: baseline and scenario totals computed in one
snapshot with the shared fragment give saved = +4,059 t/yr (a plan
regeneration was writing 796 mid-diagnosis, so values drift run-to-run;
pre-fix the same snapshot maths gives ~ -13k and worsening as plans
land). Legacy portfolio 433 totals unchanged.

No regression-test seam exists for cross-query consistency (no DB test
harness); consistency is structural — both sides compose the same
carbonSql fragment.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 14:52:21 +00:00
Jun-te Kim
767cb2af3f
Merge pull request #348 from Hestia-Homes/reporting-slow-query
amend migration files
2026-07-02 14:50:24 +01:00
Daniel Roth
8ebb0d7fec amend migration files 2026-07-02 13:48:46 +00:00
Jun-te Kim
32bf005c80
Merge pull request #347 from Hestia-Homes/reporting-slow-query
perf(db): index the EPC-graph FK columns hammering reporting
2026-07-02 14:45:40 +01:00
Daniel Roth
03ba8400ff Build the new indexes CONCURRENTLY (matches 0141)
The DB is under load; a plain CREATE INDEX blocks writes for the
duration of each build.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 13:41:12 +00:00
Daniel Roth
7eb2705817 migration files 2026-07-02 13:39:05 +00:00
Daniel Roth
4956a95574 add db indexes 2026-07-02 13:38:42 +00:00
KhalimCK
ccd32fc6bb
Merge pull request #322 from Hestia-Homes/feature/update-ui-for-new-modelling
Feature/update UI for new modelling
2026-07-02 12:50:18 +01:00
Khalim Conn-Kowlessar
051927dda4 fix(reporting): age-band and property-type breakdowns for new-approach properties
getCountByAgeBand and getCountByPropertyType read year_built /
property_type straight off the property row, which is NULL for
new-approach properties — every one fell into the Unknown bucket on
/portfolio/[slug]/reporting. Branch them like their already-migrated
siblings (getCountByEpcBand, getEstimatedCounts):

- propertyTypeSql: epc_property (lodged, else predicted) with RdSAP
  code→label mapping and dwelling_type fallback, mirroring
  resolvePropertyDescriptors; legacy stays on property.property_type.
- constructionYearSql: the main building part's construction age band
  mapped to a representative start year (correlated subquery, since
  extension parts would multiply a plain JOIN), so both paths share the
  same year→band bucketing; legacy stays on numeric year_built.
- The RdSAP label maps move into the shared-fragments section as the
  single source of truth; AGE_BAND_YEARS becomes CONSTRUCTION_AGE_BANDS
  carrying label + representative year. Band I (1996–2002) straddles the
  1976–1999 / 2000+ boundary and buckets by its start year.

Verified live against portfolio 814 (all 338 properties resolve, buckets
match the epc_building_part distribution exactly) and legacy portfolios
419/433 (output identical to the pre-fix SQL).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 11:43:32 +00:00
Daniel Roth
007ee3cad4
Merge pull request #345 from Hestia-Homes/feat/glazing-secondary-enum
Add 'Secondary glazing' to the glazing pgEnum
2026-07-02 12:18:30 +01:00
Jun-te Kim
4ec648fb1a Add 'Secondary glazing' to the glazing pgEnum
Hyde 796 landlord descriptions include '100% secondary glazing (sap 9.94)'
(39 properties), currently flattened to 'Single glazing' (U = 4.8 instead of
secondary's 2.9). The calculator already supports secondary glazing (SAP10
code 5) — only this FE-owned enum and the model repo's GlazingType lacked a
member. Additive ALTER TYPE ... ADD VALUE only, generated via drizzle-kit.

Pairs with the model-repo GlazingType.SECONDARY change (Model#1416).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-02 10:55:36 +00:00
Khalim Conn-Kowlessar
9bf36ad4b6 perf(backfill): faster, resumable recommendation denormalization + ops tooling
Overhaul the recommendation plan_id/material_* backfill for the 21M-row prod
table (see docs/runbooks/recommendation-denormalization-backfill.md):

- Single combined pass: set plan_id + all four material_* columns in one write
  per row (was two full passes, rewriting every row twice). Guard + COALESCE keep
  it idempotent and resumable — done rows are never rewritten.
- Session tuning: synchronous_commit=off, tunable work_mem / maintenance_work_mem.
- Range + phase controls (BACKFILL_START_ID/END_ID/SKIP_CARDINALITY/ONLY_FINALIZE)
  to run disjoint id-range workers in parallel when the disk isn't the bottleneck.
- Percent-complete + ETA in the progress log.
- Finalize builds the two new indexes plain by default (faster in a quiet
  window) with a CONCURRENTLY toggle.

The real speed-up in prod was dropping the 3 secondary indexes for the backfill
then rebuilding — the table is packed (fillfactor 100) so updates can't go HOT
and otherwise maintain every index per row. Index drop/rebuild + vacuum are kept
as standalone SQL so ops controls exactly when the app loses/regains them:

- sql/drop-recommendation-backfill-indexes.sql
- sql/rebuild-recommendation-backfill-indexes.sql (1GB maintenance_work_mem — safe
  on the 4GB instance)
- sql/vacuum-recommendation.sql

run-sql.ts runs .sql files statement-by-statement, autocommitted (so VACUUM /
CREATE INDEX CONCURRENTLY work), for environments without the psql CLI. Wired up
as npm run db:run-sql and backfill:recommendation-restore.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 10:00:46 +00:00
Khalim Conn-Kowlessar
1dfb1e1534 fix(building-passport): portal tooltip content so it isn't trapped by stacking context
The shadcn TooltipContent rendered inline, missing the Portal that upstream
shadcn ships. On the Current Efficiency card the info tooltip lives inside a
`relative z-10` div while the Lodged EPC badge is a sibling at `z-20`, so the
popup's own `z-50` only competed within the z-10 subtree and the badge painted
over it (also latently clipped by the card's `overflow-hidden`). Wrapping
TooltipContent in TooltipPrimitive.Portal renders it at <body>, escaping both
the stacking context and the overflow clip — fixes all 9 tooltips.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 09:56:59 +00:00
Daniel Roth
5f669046f5
Merge pull request #344 from Hestia-Homes/various-migrations
Various migrations
2026-07-02 09:57:42 +01:00
Daniel Roth
dd63f46b76 migration files 2026-07-02 08:47:01 +00:00
Daniel Roth
bd6af9b74f add a new main fuel value 2026-07-02 08:46:18 +00:00
Daniel Roth
543cb69414 3 more main_heating_system values 2026-07-02 08:44:25 +00:00
Daniel Roth
4a6a0aabba 5 new main_heating_system values 2026-07-02 08:43:43 +00:00
Khalim Conn-Kowlessar
4941e75683 fix(epc): follow energyMainsGas rename in new-approach resolver
The merge of main renamed epc_property.energyMainsGas ->
energyGasConnectionAvailable (migration 0252), but the new-approach
resolver read the old property name, which main never touched so it
survived the merge with no conflict and broke the build.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-01 15:56:16 +00:00
Khalim Conn-Kowlessar
59297c3495 Merge remote-tracking branch 'origin/main' into feature/update-ui-for-new-modelling 2026-07-01 15:34:29 +00:00
Daniel Roth
9b9a718b65
Merge pull request #342 from Hestia-Homes/main-heating-system-migration-2
New main heating system value
2026-07-01 15:45:45 +01:00
Daniel Roth
34d8301f24 migration files 2026-07-01 14:44:05 +00:00
Daniel Roth
d59aa7f079 new main heating system value 2026-07-01 14:43:39 +00:00
Daniel Roth
e63d80dd65
Merge pull request #341 from Hestia-Homes/water-heating-overrides-migration
Migration to add new water heating types
2026-07-01 15:23:38 +01:00
Daniel Roth
003913a90e migration files 2026-07-01 14:22:16 +00:00
Daniel Roth
ca781771f2 add new water heating types 2026-07-01 14:21:58 +00:00
Daniel Roth
ea4fee27d9
Merge pull request #340 from Hestia-Homes/rename-energy-column
Rename energyMainsGas to energyGasConnectionAvailable
2026-07-01 15:16:28 +01:00
Daniel Roth
eda1079e65 migration files 2026-07-01 14:08:01 +00:00
Daniel Roth
0a67aa5e4f rename energyMainsGas to energyGasConnectionAvailable 2026-07-01 14:07:26 +00:00
Daniel Roth
b2451651f5
Merge pull request #339 from Hestia-Homes/mixed-glazing-migration
Add Mixed glazing to GlazingValues
2026-07-01 13:38:18 +01:00
Daniel Roth
7a0d2a4d64 migration files 2026-07-01 12:09:14 +00:00
Daniel Roth
57d91cae1e Add Mixed glazing to GlazingValues 2026-07-01 12:08:57 +00:00
Daniel Roth
f5912070ee
Merge pull request #338 from Hestia-Homes/roof-type-migration
Roof type migration
2026-07-01 12:18:33 +01:00
Daniel Roth
6833bee645 migration files 2026-07-01 11:00:30 +00:00
Daniel Roth
c7ce0860c4 add flat room insulation values to RoofTypeValues 2026-07-01 10:59:53 +00:00
Daniel Roth
2ba084f858
Merge pull request #334 from Hestia-Homes/feature/dedupe-documents
Dedupe documents by only fetching the most recent for each S3 key
2026-07-01 09:27:18 +01:00
Daniel Roth
1969043cee
Merge pull request #336 from Hestia-Homes/not-null-lodged-on-baseline
Drop NotNull constraint on propertyBaselinePerformance lodged_ columns
2026-07-01 08:50:19 +01:00