diff --git a/CONTEXT.md b/CONTEXT.md index 46c920f8b..01623858a 100644 --- a/CONTEXT.md +++ b/CONTEXT.md @@ -473,7 +473,7 @@ The auth credential required by the New EPC API; stored in the `EPC_AUTH_TOKEN` _Avoid_: API key, auth token, secret **Void Property**: -A place Abri reports as having no live tenancy, signalled by `getSCSTenantData` returning a `RelayError` whose message is exactly `No_tenancy_found`. A normal outcome of the tenant-data sync, not a failure: there are no signatories to create as deal contacts, so the flow completes instead of dead-lettering the message, prefixing the deal's **Extra booking information** (`notes_for_surveyor`) with the `Void. ` marker. HubSpot has no append, so that property is read live and rewritten whole — never rewritten from the scraped snapshot, which lags by a scrape cycle and would discard a coordinator's unscraped edit. The marker doubles as the idempotency guard: a note already carrying it is left untouched, so redelivery never stacks markers. Distinct from a tenancy carrying no tenants (a tenancy exists; nobody is recorded against it), and from the same words arriving as a dumped 4GL query (`No Tenancy Found for Query FOR EACH ...`), which stays a rejection — Abri does not send that form to mean "known empty". +A place Abri reports as having no live tenancy, signalled by `getSCSTenantData` returning a `RelayError` whose message is exactly `No_tenancy_found`. A normal outcome of the tenant-data sync, not a failure: there are no signatories to create as deal contacts, so the flow completes instead of dead-lettering the message, prefixing the deal's **Extra booking information** (`notes_for_surveyor`) with the `Void.` marker. HubSpot has no append, so that property is read live and rewritten whole — never rewritten from the scraped snapshot, which lags by a scrape cycle and would discard a coordinator's unscraped edit. The marker doubles as the idempotency guard: a note already carrying it is left untouched, so redelivery never stacks markers. Distinct from a tenancy carrying no tenants (a tenancy exists; nobody is recorded against it), and from the same words arriving as a dumped 4GL query (`No Tenancy Found for Query FOR EACH ...`), which stays a rejection — Abri does not send that form to mean "known empty". _Avoid_: empty property, no tenancy (names the message, not the state), untenanted ## Team diff --git a/docs/abri-hubspot-ops-guide.md b/docs/abri-hubspot-ops-guide.md index 0a07c379a..ae5db8922 100644 --- a/docs/abri-hubspot-ops-guide.md +++ b/docs/abri-hubspot-ops-guide.md @@ -43,12 +43,12 @@ system. **Empty properties.** If Abri has no live tenancy for the property, no tenant contacts appear on the deal — there is nobody to add — and **Extra booking information** is -prefixed with `Void. ` so the surveyor sees it first. Anything already in that field is +prefixed with `Void.` so the surveyor sees it first. Anything already in that field is kept, after the prefix. This is expected, not a fault, and nothing needs re-triggering. Note that Abri sends the same signal for a property reference it does not recognise, so if you were expecting tenants, check the property is right before assuming it's empty. -Delete the `Void. ` prefix if you establish the property is not empty after all — the +Delete the `Void.` prefix if you establish the property is not empty after all — the system only adds it once, so it will not come back on its own. ## The fields, and why they matter diff --git a/orchestration/abri_orchestrator.py b/orchestration/abri_orchestrator.py index ef6d167e0..769a1a0a7 100644 --- a/orchestration/abri_orchestrator.py +++ b/orchestration/abri_orchestrator.py @@ -32,7 +32,9 @@ NOTES_FOR_SURVEYOR_DEAL_PROPERTY = "notes_for_surveyor" # Prepended to that property when Abri reports the place as having no live # tenancy, so a surveyor sees it first. Doubles as the idempotency marker: a # note already carrying it is left alone, so redelivery never stacks markers. -VOID_MARKER = "Void. " +# Carries no separator of its own — the write below spaces it off whatever was +# already in the note, and a note that is otherwise empty gets the bare marker. +VOID_MARKER = "Void." def _vulnerability_description(tenant: Tenant) -> Optional[str]: diff --git a/tests/orchestration/test_abri_orchestrator_tenant_data_sync.py b/tests/orchestration/test_abri_orchestrator_tenant_data_sync.py index 76b09387a..216b715be 100644 --- a/tests/orchestration/test_abri_orchestrator_tenant_data_sync.py +++ b/tests/orchestration/test_abri_orchestrator_tenant_data_sync.py @@ -585,7 +585,7 @@ def test_a_void_property_is_flagged_at_the_front_of_the_deals_booking_informatio sdk_client.crm.deals.basic_api.update.assert_called_once_with( DEAL_ID, simple_public_object_input=DealPropertyInput( - properties={"notes_for_surveyor": "Void. Key safe code 1234"} + properties={"notes_for_surveyor": "Void. Key safe code 1234"} ), ) @@ -610,7 +610,7 @@ def test_a_void_property_with_no_existing_booking_information_is_flagged_alone( sdk_client.crm.deals.basic_api.update.assert_called_once_with( DEAL_ID, simple_public_object_input=DealPropertyInput( - properties={"notes_for_surveyor": "Void. "} + properties={"notes_for_surveyor": "Void."} ), ) @@ -625,7 +625,7 @@ def test_a_deal_already_flagged_void_is_left_alone_so_redelivery_never_stacks_fl "abri_relay_getscstenantdata_notenancyfound_response.xml" ) sdk_client.crm.deals.basic_api.get_by_id.return_value.properties = { - "notes_for_surveyor": "Void. Key safe code 1234" + "notes_for_surveyor": "Void. Key safe code 1234" } # Act