Commit graph

475 commits

Author SHA1 Message Date
Daniel Roth
5fb1f92d08 migration files 2026-07-06 15:46:41 +00:00
Daniel Roth
d49042eb7e add client booking reference to hubspot deal data 2026-07-06 15:46:18 +00: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
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
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
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
Daniel Roth
8ebb0d7fec amend migration files 2026-07-02 13:48:46 +00: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
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
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
59297c3495 Merge remote-tracking branch 'origin/main' into feature/update-ui-for-new-modelling 2026-07-01 15:34:29 +00: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
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
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
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
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
782ad7b1d0 migration files 2026-07-01 07:42:14 +00:00
Daniel Roth
e36fb5398d lodged_ columns on property_baseline_performance are nullable 2026-07-01 07:41:55 +00:00
Daniel Roth
813311f535 migration files 2026-07-01 07:24:19 +00:00
Daniel Roth
8789c9bab0 extend enum 2026-07-01 07:23:36 +00:00
Jun-te Kim
f51fdca3d8 Add partial index on material(is_active) for the catalogue read
The modelling_e2e Lambda reads the active catalogue once per invocation
(SELECT * FROM material WHERE is_active ORDER BY id). Without an index this
was a full seq scan every time (~23ms), paid by all 32 concurrent Lambdas at
startup. Partial index on id WHERE is_active returns the active rows already
id-ordered — no sort, no filter.

Verified on dev: seq scan 23.2ms -> index scan 4.2ms.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-29 11:53:19 +00:00
Khalim Conn-Kowlessar
e4960db2c1 feat(epc): persist 7 calculator-read fields dropped on save
A recursive persistence-fidelity guard surfaced 7 fields the SAP calculator
reads but the epc_property projection had no column for, so they were silently
lost on save->reload (same bug class as the PV arrays / floor flags). All
additive, nullable except the PV-diverter bool; no backfill (defaults match
the domain defaults).

- epc_main_heating_detail: community_heating_boiler_fuel_type,
  community_heating_chp_fraction (community-heated CHP).
- epc_building_part: alt_wall_1_is_sheltered, alt_wall_2_is_sheltered, and
  wall_insulation_thermal_conductivity (jsonb to preserve Union[int,str]).
- epc_property: energy_pv_diverter_present (NOT NULL DEFAULT false),
  heating_cylinder_volume_measured_l, ventilation_air_permeability_ap50_m3_h_m2.

Includes generated migration 0246_minor_lady_bullseye.sql + snapshot/journal.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 18:34:45 +00:00
Khalim Conn-Kowlessar
a1548ca3a9 feat(epc): persist PV arrays and floor heat-loss flags
Add EPC persistence schema for two round-trip-fidelity gaps so the Model
backend can store the full EpcPropertyData faithfully:

- New child table epc_photovoltaic_array (one row per PV array), mirroring
  the epc_window child-table pattern.
- epc_floor_dimension: add is_exposed_floor and is_above_partially_heated_space
  boolean flags (NOT NULL DEFAULT false; additive, no backfill).

Includes generated migration 0245_magenta_nomad.sql + snapshot/journal.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-26 16:38:36 +00:00
Jun-te Kim
752ffe1935 feat(db): add property.marked_for_deletion soft-delete flag
Add a NOT NULL DEFAULT false `marked_for_deletion` boolean to the
`property` table. Hard deletes are expensive (cascading child rows,
indexes), so a row is flagged here and a separate cron job performs the
physical delete later; reads can filter on the flag in the interim.

A constant default makes this a metadata-only ALTER on Postgres 11+ (no
table rewrite); existing rows backfill to false.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-25 12:18:32 +00:00
Daniel Roth
1b7d314fdd SQL correction 2026-06-24 15:06:43 +00:00
Daniel Roth
e3a58e7e9c migration files 2026-06-24 15:02:21 +00:00
Daniel Roth
fb431a1a25 wall insulation thickness is jsonb 2026-06-24 15:01:11 +00:00
Daniel Roth
4f6537bf7e migration files 2026-06-24 13:52:46 +00:00
Daniel Roth
d0b39ecdcb add new columns to table 2026-06-24 13:52:06 +00:00
Khalim Conn-Kowlessar
ea988cfe52 feat(epc): provenance signals + effective-baseline portfolio table
Theme 1 — EPC card provenance. New resolveProvenanceSignal (pure, unit-
tested) + resolveProvenance expose provenanceSignal ("estimated" /
"remodelled" / "none") and the lodged headline on property-meta. The
building-passport card shows a "Lodged EPC" badge on re-modelled
properties (even within-band), never on predicted ones; the existing
"estimated from nearby homes" banner already covers predicted.

Theme 2 — portfolio table. "Current EPC" + reporting now read the
effective baseline (effectiveSapSql/effectiveEpcBandSql); a dedicated
"Lodged EPC" column reads the source-gated lodged value (blank for
predicted); a "Predicted" pill marks estimated rows; a provenance filter
is added and the broken currentEpc/lodgedEpc filters (on null row
columns) point at the effective/lodged baselines. Expected EPC now uses
the default plan's post rating (expectedEpcRating, unit-tested) instead
of current + Σ rec SAP, which double-counted across plans (732385: A→C);
the same rec aggregate is scoped to the default plan, fixing the Cost
column's 2x double-count.

See ADR-0002. Pure logic covered by provenance.test.ts + expectedEpc.test.ts;
SQL/query/UI verified against live properties 732385 and 729529.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-23 22:21:33 +00:00
Daniel Roth
f3c03b0ba5 migration files 2026-06-22 13:03:20 +00:00
Daniel Roth
e32bcf0d4e add property_id task source 2026-06-22 13:02:36 +00:00
Jun-te Kim
1ac4e20c4c db changes 2026-06-19 15:04:43 +00:00
Jun-te Kim
ae74133575 feat(db): add epc_property.source for predicted-EPC slot (ADR-0031)
Adds a `source` discriminator to `epc_property` so a property can hold a
lodged and a predicted EPC at the same time, per the EPC Prediction
production-wiring branch in the Model repo (docs/MIGRATION_NOTE_predicted_epc_source.md).

- `source` text NOT NULL default 'lodged' — backfills every existing row as
  a real lodged EPC.
- Unique index becomes (property_id, portfolio_id, source) so lodged +
  predicted rows can coexist for the same property/portfolio.
- New (property_id, source) index — lodged/predicted reads filter on both.

Allowed values: 'lodged' | 'predicted'.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-16 13:46:19 +00:00
Daniel Roth
10f7d4c188 migration 2026-06-15 08:59:46 +00:00
Daniel Roth
db0b3670f4 delete col 2026-06-15 08:59:24 +00:00
Daniel Roth
954fb5b16e migration 2026-06-15 08:52:41 +00:00