Commit graph

8608 commits

Author SHA1 Message Date
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
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
Daniel Roth
5309c1f097 Merge subfolder rename results into their parent folder's summary 🟩
Passed on arrival: threading the summary back up through the traversal
already carried subfolder results. Pinned so the merge cannot regress.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 08:51:36 +00:00
Daniel Roth
b738840c84 Count files skipped as already canonical apart from skipped images 🟩
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 08:51:02 +00:00
Daniel Roth
2aa698da1e Count files skipped as already canonical apart from skipped images 🟥
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 08:50:45 +00:00
Daniel Roth
ae7c9cdd46 Record each file SharePoint refused to rename, with its error 🟩
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 08:49:51 +00:00
Daniel Roth
a69d603cde Record each file SharePoint refused to rename, with its error 🟥
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 08:49:17 +00:00
Daniel Roth
9248aee139 List the UPRNs with no SharePoint folder under any root 🟩
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 08:45:46 +00:00
Daniel Roth
9e48f39fa5 List the UPRNs with no SharePoint folder under any root 🟥
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 08:45:20 +00:00
Daniel Roth
371df4d54f Count dry-run rename intents separately from real renames 🟩
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-29 08:44:37 +00:00