Commit graph

8616 commits

Author SHA1 Message Date
KhalimCK
1a9ebacb59
Merge pull request #1713 from Hestia-Homes/feat/boiler-efficiency-band
feat: honour landlord boiler SEDBUK efficiency band (5th Heating Companion) — #1704 / ADR-0068
2026-07-30 17:49:19 +01:00
Khalim Conn-Kowlessar
3e4b8bd781 fix: wire boiler_efficiency_band into the reader registry (consistency guard)
test_override_component_consistency caught two real gaps: the band was in the
override_component pgEnum but (a) missing from _ROW_TYPES — so the finaliser
couldn't load its classified vocab (a latent bug the injected-vocab finaliser
tests missed), and (b) not in _COMPONENT_OVERLAYS (correct — it's an attribute
consumed by the main_heating_system overlay, not a standalone one).

Add the band to _ROW_TYPES (reader), and introduce _ATTRIBUTE_COMPONENTS to name
the classified+stored-but-no-standalone-overlay case. The guard now asserts:
pgEnum == _ROW_TYPES; and _ROW_TYPES minus attribute components == _COMPONENT_OVERLAYS.
Full repositories suite (247) + finaliser green; pyright clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-30 16:40:46 +00:00
Jun-te Kim
0b433ac378
Merge pull request #1715 from Hestia-Homes/feature/hubspot-etl-epc-lodged-deal
Sync the epc_lodged_deal Yes/No dropdown into the HubSpot deal ETL
2026-07-30 17:26:53 +01:00
Khalim Conn-Kowlessar
50554ff57e review: backfill writes an explicit Unknown band too (matches finaliser)
Aligns the historical backfill with the go-forward finaliser: it now writes a
boiler_efficiency_band row for EVERY main_heating_system row — the parseable band
(A-G) where present, else an explicit Unknown — so historical and go-forward data
match. Unknown is fine on non-boilers (inert to modelling). Confirmed with Khalim.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-30 15:58:38 +00:00
Khalim Conn-Kowlessar
1af3860528 review: finaliser records an explicit Unknown band instead of skipping
kimjunte + Khalim alignment: when the band column is mapped and a description
can't be resolved to A-G, write the row with an explicit Unknown value rather
than silently skipping. The landlord declared the column, so every row gets a
value; 'assessed, no band' is now distinct from 'never assessed'. Still never
fails the finalise (unlike the mandatory components). Modelling reads Unknown as
no band (no efficiency anchor), so it stays inert to SAP.

Requires the FE boiler_efficiency_band pgEnum to include 'Unknown' (assessment-
model#486 updated). 7 finaliser tests green; pyright clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-30 15:05:27 +00:00
Khalim Conn-Kowlessar
eb07b91614 review: guard-only band classifier, backfill count, docstring (PR #1713)
Addresses reviewer feedback:
- #3 (Khalim + kimjunte): the boiler_efficiency_band classifier column is now
  GUARD-ONLY — LLM fallback removed. The band format is deterministic, so an LLM
  guess could only fabricate a band the landlord never stated (moves SAP +
  eligibility). New LoggingUnknownColumnClassifier is the non-fabricating fallback:
  maps guard-misses to UNKNOWN (never stored) and logs them for review — kimjunte's
  'warn on None', placed in the classifier path not the shared guard (which sees
  legitimate None constantly on the modelling/backfill paths).
- #2 (Khalim): backfill --apply now reports rows ACTUALLY written (upsert
  rowcount), not the candidate count, so an idempotent re-run reports 0.
- #1 (Khalim): overlay module docstring scrubbed of stale 'slot/pending' wording
  to match the cert-native anchor mechanism.

kimjunte's finaliser optional-skip question (declared-vs-shared-column flow) left
for reviewer alignment, not changed. 529 tests green; pyright clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-30 14:57:11 +00:00
Khalim Conn-Kowlessar
10acaa48cc docs(adr): ADR-0068 mechanism — cert-native anchors, not a synthesis slot
Decision 4 + Status rewritten: band → Table 4b code (C-G) / PCDB index (A/B),
carried by cert-native fields only. Records the review rejection of the
(winter,summer) slot on MainHeatingDetail.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-30 13:08:08 +00:00
Khalim Conn-Kowlessar
307fdfb270 refactor(overlay): band uses cert-native anchors, not a synthesis field
Reviewer feedback: seasonal_efficiency_override_pct had no lodged-cert analogue
and polluted the EpcPropertyData/MainHeatingDetail cert datatype. Replace it with
the cert-native efficiency anchors a real cert already uses:
- C–G  -> sap_main_heating_code = the band's Table 4b code (combi/regular
  preserved by the code itself); resolved by the existing Table 4b path.
- A/B  -> main_heating_index_number = a type-matched representative PCDB product;
  resolved by the existing PCDB Appendix D2.1 path (winter+summer+combi-loss).
  _fold_heating clears the base code, so the effective cert reads as a real PCDB
  cert. Ready for future user-supplied product -> PCDB id.

Deletes seasonal_efficiency_override_pct from MainHeatingDetail, HeatingOverlay
and _MAIN_HEATING_FIELDS, and REVERTS both cert_to_inputs branches — those four
files are now byte-identical to main (no synthesis on the cert type, no new
calculator branch). Each A/B PCDB id is CI-guarded to its accredited (winter,
summer) so a PCDB refresh fails loudly. Oil A-combi (no >90% product, ≈0 homes)
keeps its code-130 default. Real certs: A-combi +2.06 SAP (idx 18964), D-regular
-0.98 (code 106); corpus unmoved; 810 tests green (ADR-0068).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-30 13:06:18 +00:00
Jun-te Kim
57a531fc04 Sync the epc_lodged_deal Yes/No dropdown into the HubSpot deal ETL
Adds parse_hs_yes_no rather than reusing parse_hs_bool: that one reads a
HubSpot checkbox ("true"/"false") and would call "Yes" false. Anything
that is neither yes nor no parses to None, so an unanswered dropdown
never reads as "answered No".

The differ gets its own yes_no_fields block alongside bool_fields, since
the two lists need different parsers.

The epc_lodged_deal column still has to be created by a migration in
Hestia-Homes/assessment-model before this can write.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-30 10:01:08 +00:00
Khalim Conn-Kowlessar
df9073d80a docs(adr): ADR-0068 status — A/B validated and live for gas + oil
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-29 19:08:43 +00:00
Khalim Conn-Kowlessar
9760b38238 feat(overlay): pin oil A/B bands from accredited PCDB (completes A/B matrix)
Oil A = (90.6, 82.8) [PCDB 17292 Firebird Enviromax Blue Supreme], B = (88.0, 80.2)
[PCDB 10498 Worcester Greenstar Danesmoor], applied to oil regular (127) + combi
(130). No separate oil Elmhurst build: the gas builds proved Elmhurst's worksheet
(206)/(217) for a database boiler == its PCDB winter/summer read verbatim (SAP 10.2
App D2.1), so the PCDB record is the accredited value. Corpus unmoved; 68 overlay
tests green (ADR-0068). Full A/B matrix (gas/LPG/biogas + oil) now live.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-29 19:07:57 +00:00
Khalim Conn-Kowlessar
063eb17a89 feat(overlay): enable gas A/B bands — Elmhurst-validated (winter,summer)
Gas A = (90.6, 81.6) [PCDB 18790 Worcester Greenstar 8000 Life], B = (88.0, 79.4)
[PCDB 15029 Baxi Duo-tec Combi 24 HE], read off accredited Elmhurst RdSAP10
worksheets (206)/(217). Applied to gas regular (102) + combi (104); gas/LPG/biogas
share the codes. A-rated real cert 15017550: +1.78 SAP (the under-credit fix).
Oil A/B still pending (no slot). Corpus unmoved; 797 tests green (ADR-0068).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-29 18:37:10 +00:00
Khalim Conn-Kowlessar
c7edb1df0b test(elmhurst): A/B-band validation certs + Elmhurst input sheets (ADR-0068)
A-band: uprn 15017550 (gas combi lodged at generic 84% code 104 — the A under-
credit). B-band: uprn 37020227 (already lodges a real B-rated boiler, PCDB 9900
Ideal isar 88.1/79.5). Sheets carry the special instruction to report Elmhurst's
SAP + boiler winter/summer efficiency, to pin _BAND_SLOT_EFFICIENCY_PCT.

Candidates from accredited PCDB: A gas ~(90.0, 80.0), B gas ~(88.0, 79.5).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-29 17:09:20 +00:00
Khalim Conn-Kowlessar
c35fb0afb9 docs(adr): ADR-0068 accepted+implemented; CONTEXT Boiler Efficiency Band glossary
Folds the design docs (previously PR #1705, docs-only) into the implementation PR
so it is self-contained: ADR-0068 status -> accepted/implemented, and the CONTEXT
glossary gains the Boiler Efficiency Band term + the fifth-Heating-Companion note.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-29 16:40:50 +00:00
Khalim Conn-Kowlessar
c690cd3183 feat(backfill): boiler_efficiency_band rows from existing heating descriptions
Idempotent, dry-run-by-default script (mirrors reclassify_main_heating). Parses
the SEDBUK band off each main_heating_system row's original_spreadsheet_description
with the SAME guard as the live classifier (no drift), upserting a
boiler_efficiency_band row per boiler that carries one. Pure core band_backfill_rows
unit-tested. FE-enum-gated writes (Class-A/B deferred) (ADR-0068).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-29 16:34:53 +00:00
Khalim Conn-Kowlessar
6a4039c486 feat(classifier): GREEN — classify + finalise the Boiler Efficiency Band
New boiler_efficiency_band classifier column (handler.py) reading the shared
Heating source header via the deterministic guard (LLM fallback -> UNKNOWN),
cached in landlord_boiler_efficiency_band_overrides. override_component mirror +
cache-table pgEnum are FE-owned (deferred/Class-A/B — no deploy until the Drizzle
migration lands). Finaliser treats the band as an OPTIONAL component: UNKNOWN ->
skip (no row), not fail-loud like the mandatory ones (ADR-0068).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-29 16:32:42 +00:00
Khalim Conn-Kowlessar
291717ca8f test(finaliser): RED — boiler_efficiency_band alongside heating; UNKNOWN band skips
The band rides the Heating source column and yields its own override row; unlike
the mandatory components an UNKNOWN band is a legitimate no-value and must be
skipped, not fail the finalise (ADR-0068).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-29 16:26:47 +00:00
Khalim Conn-Kowlessar
ce803ecedc feat(overrides): GREEN — thread Boiler Efficiency Band to the overlay call site
ResolvedPropertyOverride now carries original_spreadsheet_description (populated
by both postgres readers). overlays_from builds the main-heating overlay directly
via _main_heating_overlay, resolving the band: an explicit boiler_efficiency_band
row wins, else parse-on-read from the heating description (ships before FE). Band
applied after the ADR-0067 fuel re-point. 28 overlay tests green, pyright clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-29 16:21:14 +00:00
Khalim Conn-Kowlessar
e91e714221 test(overrides): RED — thread the Boiler Efficiency Band to the overlay call site
Parse-on-read from the main_heating_system row's original_spreadsheet_description
(ships before FE); an explicit boiler_efficiency_band override row wins when
present; band applied after fuel resolution (oil re-point); no band -> no slot.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-29 16:18:53 +00:00
Khalim Conn-Kowlessar
72840cbdf7 feat(calculator): GREEN — consume overlay (winter,summer) ahead of Table 4b code
_main_heating_detail_efficiency uses the override winter (§206) and the Eq D1
water branch uses the override (winter,summer), both ahead of the PCDB/Table 4b
defaults — the same precedence the PCDB winter_efficiency_pct occupies. Guarded
on WHC 901 + not-electric-immersion for the water leg. Override-supplied only, so
the lodged-cert corpus is unmoved (ADR-0068). 256 calculator tests green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-29 16:16:09 +00:00
Khalim Conn-Kowlessar
be0fb08e65 test(calculator): RED — overlay (winter,summer) slot wins over Table 4b code
The override-supplied seasonal efficiency must take precedence in both the §206
winter path (main_heating_efficiency) and the Appendix D Eq D1 summer water path,
the same precedence slot the PCDB winter_efficiency_pct occupies (ADR-0068).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-29 16:13:39 +00:00
Khalim Conn-Kowlessar
4244b16d17 feat(overlay): GREEN — band sets a (winter,summer) efficiency slot on gas/oil boilers
band_seasonal_efficiency_pct maps (base_code, band) -> Table 4b (winter,summer)
for C-and-below; A/B pending Elmhurst (empty slot dict, no invented numbers);
non-banded boilers (solid/electric/CPSU) return None. main_heating_overlay_for
takes an optional band and sets seasonal_efficiency_override_pct, threaded onto
MainHeatingDetail via _fold_heating (ADR-0068). Calculator consumption next.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-29 16:10:07 +00:00
Khalim Conn-Kowlessar
7a35d177b2 test(overlay): RED — band -> (winter,summer) slot on gas/oil boiler overlay
C-and-below reuse accredited Table 4b pairs; A/B pending Elmhurst (no slot yet,
no invented numbers); non-banded boilers (solid/electric/CPSU) ignore the band.
The base code is unchanged; only the efficiency slot is set (ADR-0068).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-29 16:06:43 +00:00
Khalim Conn-Kowlessar
b41c25e756 feat(overrides): GREEN — BoilerEfficiencyBand enum + SEDBUK band parser
BoilerEfficiencyBand (A-G + UNKNOWN sentinel) and boiler_efficiency_band_guard,
the shared deterministic parser for the fifth Heating Companion (ADR-0068).
UNKNOWN is never persisted; absence of a band -> no value -> Table 4b default.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-29 16:04:06 +00:00
Khalim Conn-Kowlessar
4844d030d7 test(overrides): RED — BoilerEfficiencyBand enum + SEDBUK band parser
Boiler Efficiency Band is the 5th Heating Companion (ADR-0068). The shared
parser extracts the SEDBUK A-G band from the Landlord 'Heating' description
(`Boiler: <A-G> rated ...`), primary-system-1 on multi-system cells, None
when absent. Single source of truth for read/backfill/classifier.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-29 16:03:14 +00:00
Jun-te Kim
141c783e40
Merge pull request #1711 from Hestia-Homes/fix/sap-full-community-dhw
Bill full-SAP water-only community hot water on the heat-pump network (re-opens #1692)
2026-07-29 16:49:46 +01:00
Jun-te Kim
269fcf6e47 Persist the water-only community DHW network across the DB round-trip 🟩
The two new `SapHeating` fields were mapped from the API but dropped by
`EpcPostgresRepository._compose`, which the ADR-0036 structural guard caught.

Allow-listing them would have been wrong. `PropertyBaselineOrchestrator.run`
(applications/modelling_e2e/handler.py:885) always re-derives the persisted
baseline from the composed object, so an unpersisted field means the deployed
baseline keeps pricing heat-pump DHW as direct electric — the Dovestone cert
would still land at SAP 67 even with the mapper fix in place. Several plan
paths read the stored EPC too (refetch_epc=False, no lodged cert, stored cert
newer, stored survey wins).

Round-trip fidelity also matters for churn: `_reconcile_lodged` compares
`fetched != stored`, so a field that survives the API mapper but not `_compose`
makes that inequality permanently true and re-saves every public cert forever.

Needs the matching Drizzle columns in assessment-model before deploy.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-07-29 14:32:57 +00:00
Jun-te Kim
3fe4c3856a Record exact engine↔Elmhurst convergence at SAP 80 for the Dovestone cert 🟪
A faithfully-built Elmhurst RdSAP10 (AP50 3.2, MVHR unit 500167, phantom
secondary + open-flue cleared) reproduces the engine EXACTLY: worksheet 80 =
engine 80, fabric heat loss 41.2 vs 41.6 W/K. The earlier 74 was Elmhurst
data-entry fidelity (four artefacts), not calculator error. Refresh the saved
worksheet/summary PDFs to the SAP-80 build; pin unchanged at 80.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DX8oAGsGkBHq3U4dsxYRzz
2026-07-29 14:11:52 +00:00
Jun-te Kim
2f8c086310 Record the DHW-energy residual investigation: not a bug 🟪
Root-caused the 2675-vs-1839 kWh communal-DHW delta: the engine correctly
applies the SAP §4.3 distribution-loss factor (1/(1.05×1.50)); the difference is
the DHW energy content (64) alone. The engine's 1698 kWh tracks the cert's own
lodged RHI water heating (1909) far better than Elmhurst's reduced-data 1168,
and its £113 is closer to the cert's lodged £147 than Elmhurst's £78 — so the
engine is more faithful to the cert here, not buggy. Comment accuracy only.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DX8oAGsGkBHq3U4dsxYRzz
2026-07-29 14:11:52 +00:00
Jun-te Kim
6e6c27076e Correct the DHW-energy residual note: no cylinder loss on communal DHW 🟪
The dwelling has no hot-water cylinder (has_hot_water_cylinder false), so the
2675-vs-1839 kWh DHW-energy gap is not a cylinder storage loss as previously
stated — it is an un-root-caused SAP §4.3 community-DHW demand/distribution-loss
difference. No behaviour change; comment accuracy only.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DX8oAGsGkBHq3U4dsxYRzz
2026-07-29 14:11:52 +00:00
Jun-te Kim
db274adcb3 Pin the Dovestone water-only community DHW cert recovering to SAP 80 🟩
Full-SAP SAP-Schema-18.0.0 cert 9556-3047-3304-5355-1200 (Dovestone Gardens
Apt 4): communal heat-pump DHW + direct-electric room heaters. Was 67 (D) with
DHW mis-billed as direct-electric immersion; now 80 with DHW on the community
heat-pump fuel. Accredited Elmhurst worksheet 74 (evidence saved); the +6 is the
documented full-SAP measured-U/MVHR vs RdSAP age-band residual.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DX8oAGsGkBHq3U4dsxYRzz
2026-07-29 14:11:52 +00:00
Jun-te Kim
20268a4a6d Bill water-only community DHW on the heat-pump network fuel 🟩
A water-only community scheme (water_heating_code 950/951/952) is served
by its own heat network, independent of the space main. _water_heating_fuel_code
now resolves the network's SAP Table 12 community fuel (e.g. 41 = heat from
electric heat pump @ 4.24 p/kWh, COP already priced into the row) instead of
defaulting to the space main's fuel. For a direct-electric main this had
mis-billed communal heat-pump DHW at the 13.19p standard-electric rate.

Dovestone Apt 4 (cert 9556-3047-3304-5355-1200): hot-water cost £353 → £113,
SAP 67 → 80.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DX8oAGsGkBHq3U4dsxYRzz
2026-07-29 14:11:52 +00:00
Jun-te Kim
c89843e384 Map full-SAP water-only community DHW heat source onto the domain 🟩
Water-only community hot water (water_heating_code 950/951/952,
community_heating_use 2) carries its own heat source in
sap_community_heating_systems, independent of the space main. Expose the
network's heat-fraction-weighted efficiency (COP×100) + Table-12 fuel on
SapHeating so the calculator can price DHW on the network instead of
dropping it to a direct-electric immersion cylinder.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DX8oAGsGkBHq3U4dsxYRzz
2026-07-29 14:11:52 +00:00
Daniel Roth
91e2591de5
Merge pull request #1708 from Hestia-Homes/fix/pin-aws-provider-6.56.0
Pin the AWS provider to 6.56.0 — 6.57.0 breaks deploys
2026-07-29 14:02:20 +01:00
Daniel Roth
8e9c1c257d Pin the AWS provider to 6.56.0 across every Terraform stack
v6.57.0 (released 29 July 2026) breaks ordinary reads across unrelated AWS
services — SSM GetParameter returns SerializationException, IAM GetPolicy
returns a 302, ECR returns InvalidSignatureException — with no config
change. Reported upstream as hashicorp/terraform-provider-aws#49170, open
with no root cause identified; 6.56.0 is confirmed good.

Every stack declared `>= 5.0` with no upper bound and no lock files are
committed, so each CI run silently resolved whatever HashiCorp had shipped
most recently. That is how a provider released today broke a pipeline
nobody had touched. Pinning exactly makes the deployed version a reviewed
decision rather than a discovery.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 10:46:45 +00:00
Daniel Roth
72944dccab
Merge pull request #1706 from Hestia-Homes/feature/renamer-task-handler
SharePoint Renamer: put the run on the app-owned-task lane
2026-07-29 11:22:49 +01:00
Daniel Roth
292bd7e17d Restore the Abri ops guide to main
An unrelated in-progress edit that was already in the working tree when
this branch's work started, swept in by a bare `git add -A`. It belongs to
its author, not to this PR.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 10:09:39 +00:00
Daniel Roth
3e6464fb15 Bind a property's rename callback without a default-argument trick 🟪
partial binds plan eagerly and without introducing a parameter the caller
never passes, so there is no late-binding question to reason about.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 10:05:29 +00:00
Daniel Roth
35a3ebe0e3 Complete a property whose file SharePoint rejected 🟩
Green on arrival — rename() records rejections rather than raising.
Pinned after verifying it bites: letting the rejection escape fails the
child sub_task, which turns the whole Task red.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 09:53:58 +00:00
Daniel Roth
5b1b319c2d Give each property that needs renaming its own sub_task 🟩
Properties are planned and renamed one at a time, so only those with work
get a row and a run killed by the timeout still leaves every property it
reached both renamed and recorded.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 09:52:30 +00:00
Daniel Roth
cc07cc0114 Plan each property's renames before applying any of them 🟩
The walk now yields a PropertyPlan per address-list row, and rename()
applies one. run() composes the two, so single-pass callers are unchanged.
Knowing a property's size before the first rename is what makes "when is
this property finished?" answerable.

Tests that reached through the old private _process_folder are rewritten
against the public run(), so they survive this kind of change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 09:50:28 +00:00
Daniel Roth
737d07a951 Plan each property's renames before applying any of them 🟥
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 09:47:20 +00:00
Daniel Roth
bf1f5693be Build the handler test's address list without a shared mutable default 🟪
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 09:21:11 +00:00
Daniel Roth
bbb3c9e057 Verify a local rename run the way the queue delivers it 🟪
The local invoke now sends an SQS-shaped payload — a direct invoke is the
one shape that cannot reproduce the dry_run bug class. Test modules route
protected-method calls through typed helpers so both pass pyright strict.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 09:19:51 +00:00
Daniel Roth
9870005230 Ship the Renamer image and infrastructure with database access 🟩
The task lane pulls the task domain types, both task repositories and four
Postgres modules into the image, plus SQLAlchemy, SQLModel and a driver.
Terraform gains the DB credentials block and five Postgres env vars; the
deploy job gains the three DB secrets the shared workflow already declares.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 09:11:41 +00:00
Daniel Roth
be1d6faea0 Complete a rename run that hit locked files or missing folders, and reject an unusable site 🟩
Green on arrival: both fall out of the summary the run now returns and of
the guarded site lookup. Pinned after verifying they bite — an unguarded
DomnaSites[name] completes an "ECO" run that renames files under a
different site's alias.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 09:03:43 +00:00
Daniel Roth
0836b18d5b Attribute a rename run to its request and target site 🟩
Green on arrival: @task_handler records inputs and reads source_id from
the body key matching the source's value. Pinned because it is the only
place the new sharepoint_site member is proven against a real Postgres
enum rather than a Python one.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 09:01:51 +00:00
Daniel Roth
1213b80e3f Honour dry_run on a queue-delivered rename request 🟩
Green on arrival: reading the request from the SQS record body is what
taking the task lane gave us. Verified the guard bites by restoring the
raw-event read, under which the "dry" run renames a live file.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 08:59:45 +00:00
Daniel Roth
694881c110 Record a rename run's result summary on its sub_task 🟩
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 08:58:47 +00:00
Daniel Roth
e7785f6b76 Record a rename run's result summary on its sub_task 🟥
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 08:58:18 +00:00