Commit graph

15 commits

Author SHA1 Message Date
KhalimCK
c861d4a0b0
Merge pull request #353 from Hestia-Homes/feature/portfolio-scenarios
feat(scenarios): portfolio Scenarios tab — gallery, detail, and creation journey
2026-07-06 16:21:21 +01:00
Khalim Conn-Kowlessar
158d5ded92 fix(postcode-search): PR #355 review feedback
- Order address candidates naturally so Flat 2 sorts before Flat 10
  (TDD'd in the domain core; numeric locale compare).
- Disable spell check / autocomplete / autocorrect on the postcode input.
- Long result lists and the tray's postcode chips now scroll inside
  their containers instead of growing the page.
- gitignore docs/wip/** and untrack the postcode-search handover doc.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 15:13:49 +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
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
Khalim Conn-Kowlessar
639cee6c4c feat(scenarios): portfolio Scenarios tab — gallery, detail, creation journey
Users could only create scenarios buried inside the CSV-upload modal, at
trigger time. This adds a first-class Scenarios tab: a gallery of the
portfolio's scenarios, a read-only detail page, and a 3-step creation
journey (goal → measures → review) that saves app-authored scenario rows
per ADR-0003.

Domain rules (TDD'd in src/lib/scenarios/model.test.ts, 19 tests):
- goal semantics: "Increasing EPC" requires a target band; all other
  goals are maximise-within-budget with goal_value forced null
- exclusions-only measure model: normalised (dedupe+sort), unknown keys
  rejected, all-excluded rejected, empty set valid; serialised to the
  backend's brace text format (parses the legacy space-padded variant)
- insert mapping: multi_plan=true, is_default=false, budget per property
- exact-config duplicate detection ignoring name (review-step warning)
- status derived from plan existence — no stored status column

Queries follow the ADR's discipline: gallery status = one grouped
COUNT(DISTINCT property_id) per portfolio; detail = one scoped count
(index-only, powers the blocked-delete message); delete = single atomic
DELETE guarded on NOT EXISTS(plans). Rename is the only other mutation —
configuration is immutable.

Verified live on portfolio 814: create (400 on missing band, 201 on CO₂
draft), derived Awaiting/Modelled in the gallery, rename, delete of the
draft (200) and blocked delete of the modelled scenario (409, "338
properties…" matching the portfolio's true count).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-06 08:34:47 +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
Jun-te Kim
f6545c2fa0 property override 2026-06-08 09:38:48 +00:00
Jun-te Kim
744bfa2287 Add v2 handover doc for property_overrides population
Self-contained brief to start a fresh context implementing the per-Property fact
layer in the bulk_upload_finaliser: locked decisions, the four-input assembly,
the two open hazards (classifier-CSV↔combiner-output join key; property_id for
no-UPRN rows), candidate architectures, and a first-steps list. v1 (async
finalise writing `property`) is shipped and working.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 09:38:48 +00:00
Jun-te Kim
87aca96dfa bulk upload finaliser 2026-06-08 09:38:48 +00:00
Jun-te Kim
dff86b1ec2 save current progress 2026-06-08 09:38:48 +00:00
Jun-te Kim
65a5f57321 Confirm building-part ordering on awaiting_review (#297)
Adds the multiEntryOrdering jsonb column + interactive order picker: the
largest-count multi-entry sample is shown with a building-part dropdown per
file position (one Main building + Extensions), validated as a permutation.
A PATCH route persists { count: permutation } + confirmed, and Finalise is
disabled until the ordering is confirmed when the upload is multi-entry.

Migration for the new column is intentionally not included here — generated
after origin/main is merged (its multi_entry_summary migration lands first).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 09:38:48 +00:00
Jun-te Kim
052ab446e0 merged from main 2026-06-08 09:36:26 +00:00
Jun-te Kim
4981b0ee12 save plans for landlord overrid 2026-06-08 09:35:29 +00:00
Khalim Conn-Kowlessar
9c569f5584 Add SES observability foundation for email auth (PR 1 of code-fallback)
Wires the X-SES-CONFIGURATION-SET header on outbound auth emails so SES
bounce/delivery events flow through dev-ses-config to the dev-ses-events
SNS topic. Replaces the fire-and-forget "EMAIL MAGIC LINK SENT" log
(which fired before the SMTP transaction and swallowed downstream errors)
with structured EMAIL_MAGIC_LINK_SUCCESS/_FAILURE logs carrying the
Nodemailer messageId, so app-side sends are now correlatable with SES
events.

Motivated by the Atkins / Sustainable Building UK silent-quarantine
incidents where we couldn't tell whether SES had even tried to send.

Plan doc at docs/wip/auth-email-code-fallback-plan.md tracks the
broader email-code-fallback design that PR 2 will implement.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-27 13:15:25 +00:00
Jun-te Kim
7a29398aba save local branch 2026-05-27 10:14:18 +00:00