Repository navigation
docs: describe the property v2 match, limit and score changes - #65
Conversation
562319e to
728892f
Compare
nkotolenko-cpu
left a comment
There was a problem hiding this comment.
Checked the claims against the published OpenAPI rather than reading them for
plausibility, and the great majority hold exactly. match_type really is
["exact", "best match"], space and all. OwnerOccupancyEnum is precisely
OwnerOccupied / AbsenteeOwner / Unknown. Phone types are the five
lowercase values listed. The metadata blocks really are named
person_owner_metadata on owners and resident_metadata on residents. The
confidence_score 100/90/50/25/null table matches the spec's own field
description almost word for word, and strict_match is a real query parameter
backed by a real StrictMatchRequiresStreetError. This reads as written from
the spec rather than from memory, which is not the norm for a docs backfill.
Four notes inline, none blocking. Two of them are questions I could not settle
from outside: whether strict-match no-match really returns 404, and whether
the contact cap is 4/3 or the worked example is right — those two disagree with
each other. The other two are a markup leak from the generator and a migration
row that went stale in the same way as the two this PR fixes.
The banner commit deserves a note of its own: changing the id alongside the
copy is the part that is easy to miss, and getting it wrong would have meant
nobody who dismissed the April banner ever saw this one.
Review summary
Passes: Architecture 1 | DRY 0 | Security 0 | Nits 1 | Commits 0 | Testing 0 | Correctness 2 | Cross-PR 0 | Historical 0
- Change size: medium (+311/-65 across 6 files)
- Coherence: single-purpose, split into a content commit and a banner commit
- Commits: atomic, conventional, bodies that explain the reasoning
- Risk: low for the site, medium for readers — the 404 and the cap numbers are
contract claims callers will code against - Score: 88/100
Constraints applied: spec-side gaps are chased separately from docs PRs, so
everything here is a note rather than a merge condition.
Findings: 0 critical | 0 warnings | 4 suggestions
Queued: 4 of 4
Ruled out by constraint: none
Cross-PR: this is the only open PR on the repo.
|
|
||
| If a wrong property is worse than no property — an automated pipeline, say, or | ||
| address verification — set `strict_match=true`. The API returns the address you | ||
| searched for or an address in the same building, and `404` when it finds |
There was a problem hiding this comment.
The published spec doesn't declare a 404 on this operation — GET /v2/property/ lists 200, 400, 403, 422, 500. It's not that the spec skips 404s generally: ten other operations declare one, including GET /v2/property/{property_id}.
Two readings and I can't tell which from here. Either the endpoint does 404 and the spec is missing the response — though someone did touch this operation for the release, since strict_match and StrictMatchRequiresStreetError are both in there — or a strict no-match comes back as { "result": null } with 200 like the ordinary no-match, and the 404 belongs only to the details endpoints.
Worth pinning down because the reference page for this same endpoint renders its response table live from that spec, so the guide and the page beside it will disagree on the error contract. Same claim in the changelog entry, and the migration guide's "Address-based no-match → 200" row would want a strict-match exception if 404 is right.
There was a problem hiding this comment.
Settled it against the running API rather than the spec: a no-match address search returns 404 with an error envelope, not { "result": null } at 200. So your first reading is right — the endpoint does 404 and the spec is missing the response.
It isn't new to strict matching either. The address search has always raised not-found on a zero-result search; strict_match just reaches it by another route. The gap is that the search operation declares no responses at all, while the details operation declares 404 explicitly — which is why one has it and the other doesn't. That's an upstream fix and I've noted it.
Your last point was the useful one: the migration guide's "Address-based no-match → { "result": null }, HTTP 200" row was simply wrong, and wrong independent of this release. Corrected to 404, with the strict-match case called out.
|
|
||
| ## Contact Limits on Owners and Residents | ||
|
|
||
| Each owner and resident carries up to **4 phone numbers** and **3 email |
There was a problem hiding this comment.
The cap and the worked example disagree. With a cap of 4 phones, a person holding 7 should come back as displayed: 4, additional: 3 — the example shows displayed: 2, additional: 5. Emails have the same problem: cap 3, example displayed: 2, additional: 1, so 3 total with one withheld.
The spec is unambiguous about the arithmetic — ListCountDto.additional is "upstream_total minus displayed" — but carries no maxItems and no prose about 4 and 3, so I can't tell whether the caps or the example are the wrong half. Same block appears in the changelog and twice in the sample response above.
There was a problem hiding this comment.
You're right and the example was impossible: with a cap of 4, displayed: 2 means the person holds 2 phones, so additional can only be 0. I'd made the numbers up to look illustrative instead of taking them from a real response.
The caps are the correct half — 4 and 3, enforced in the truncation helpers. The sample person now sits at the caps so displayed matches the array lengths: 4 with additional: 3 for phones, 3 with additional: 1 for emails, in the guide sample, the standalone block and the changelog.
Then I swept every metadata example in the repo for the same defect and found one more, pre-existing in the Person Search guide: phones: { "displayed": 1, "additional": 3 } and historic_addresses: { "displayed": 1, "additional": 5 }, both unreachable for the same reason. Fixed those in the same pass — outside this PR's nominal scope, but leaving a known-false example next door seemed worse than the small scope bump.
| Pass ``strict_match=true`` to receive only the address supplied or | ||
| an address in the same building. | ||
|
|
||
| Raises |
There was a problem hiding this comment.
The endpoint docstring's reStructuredText came through the generator intact. Raises followed by ------ is a setext H2 in markdown, and ``strict_match`` renders as literal double backticks rather than inline code. It's in structuredData.contents too, so the search index carries it.
Fix is upstream in the API docstring — plain prose instead of an RST Raises block — but flagging it here since this regeneration is what brings it in.
There was a problem hiding this comment.
Agreed, and it's upstream — the endpoint docstring is the OpenAPI description verbatim, so the Raises block and the double backticks go straight to the published spec and, as you say, into structuredData.contents.
Worth adding that it isn't only this endpoint: person search already ships the same shape with StrictMatchWithFuzzyMatchingError, committed before this PR. So it's a pattern in the route docstrings rather than a one-off, and the fix wants to cover both.
I've left the regenerated page matching the spec rather than hand-editing it, since a hand edit would only come back on the next generation.
| | `id` | `result.property_id` | Renamed for clarity. Same prefix (`R`) + 10 alphanumeric characters. | | ||
| | `property_address.id` | `result.property_address.address_id` | Renamed. Same value. | | ||
| | `property_address.full_address` | `result.property_address.full_address` plus `.city` / `.state` / `.line1` / `.zip` | Same value, plus a structured breakdown of the components for easier downstream use. The breakdown fields are populated for any well-formed address. | | ||
| | `owners[]` | `result.ownership_info.person_owners[]` (+ `business_owners[]`) | Owners are now split into person vs. business under an `ownership_info` umbrella. The person-owner object's shape (id, name, current_addresses, phones, emails) is the same. | |
There was a problem hiding this comment.
This row went stale in the same way as the two below it that the PR corrects. The person-owner shape isn't "the same" any more — it gained age, company_name, job_title and person_owner_metadata, and its phone type casing changed. The residents[] row two rows down says "Same shape and semantics" and has the same problem.
There was a problem hiding this comment.
Fixed, and thank you for catching that I'd done half the job. The owners[] row now lists the fields the object actually carries rather than claiming the shape is unchanged, and the residents[] row no longer says "same shape and semantics" — it points at resident_metadata as the one difference from owners.
Property search now says whether the property it returned is the address that was asked for, caps contact lists per person, and carries scores, age and employment on owners and residents. None of it was documented, and several migration claims became false when it shipped. Add Match Confidence, strict_match and Contact Limits sections to the Property Search guide, and bring its sample response up to the shape the endpoint returns. Describe displayed and additional in the words the Person Search guide already uses, so a reader comparing the two endpoints meets one definition. Correct four rows in the v1-to-v2 mapping. Owner phone confidence is surfaced, a metadata block does exist, the person-owner shape is no longer the v1 shape, and an address-based no-match returns 404 rather than an empty 200 — the last verified against the running API, and wrong since before this release. Every displayed/additional example now holds arithmetically: a list only withholds items once it reaches its cap, so a count below the cap cannot have more waiting. One example in the Person Search guide broke that rule too and is corrected here rather than left standing. Note in the agentic entity-shapes guidance that an embedded person carries counts and a details_url, so an agent can tell whether hydrating is worth a request. Record all of it in the changelog, including the phone shape change: type and number on property records now match the person endpoints, so callers parsing either field need to update. Regenerate the property reference page from the published OpenAPI.
The banner still advertised pagination and fuzzy matching, both from April, so the first thing a reader saw on every page was five months stale. Name the property search changes instead, and change the banner id. The id is the dismissal key, so a reader who dismissed the April banner would never see a new one while it stayed the same.
728892f to
b69ccb5
Compare
nkotolenko-cpu
left a comment
There was a problem hiding this comment.
Re-reviewed against b69ccb5. All four notes from the last pass are answered,
and three of them are fixed in the diff.
The cap arithmetic is right everywhere now — every displayed matches its
array length, and additional is non-zero only where the list sits at the cap.
That holds across the guide sample, the standalone block and the changelog. The
sweep into the Person Search guide was worth doing; its two examples were
unreachable in the same way, and the person migration guide was already
self-consistent, so that class is closed.
The migration owners[] and residents[] rows now describe the object the
endpoint returns.
Leaving the generated reference page alone is the right call. The Raises
block comes from the endpoint docstring, so a hand edit returns on the next
generation, and person search already ships the same shape — an upstream fix
covering both routes.
Approving. Two things to pick up, neither blocking.
The 404 correction landed in one of three places.
content/docs/references/migration/property-v1-to-v2.mdx now says a no-match
is 404 with an error envelope. Two rows in the same file still describe the
old contract — the Search table's top-level row ("null (no match) is now
expressed as { "result": null }") and the behavioral table's response-shape
row ({ "result": record | null }). A reader who starts at either one codes
for a 200 and gets a 404. The Search table row sits in the block this PR
rewrote.
owned_properties.displayed counts something the response does not carry.
The owner object in the sample has no owned_properties array, and the
metadata reports displayed: 1. The guide defines displayed as the count of
items in the list on this response. The new callout covers additional but
leaves displayed meaning one thing for phones and emails and another here.
Why
Property search now reports whether the property it returned is the address that was asked for, caps the contact lists on each owner and resident, and carries confidence scores, age and employment on those people. None of that was documented. Two rows in the v1-to-v2 mapping also became false when it shipped, telling readers that owner phone confidence is not surfaced and that no metadata block exists.
What Changed
The Property Search guide gains Match Confidence,
strict_matchand Contact Limits sections, and its sample response is rebuilt to the shape the endpoint actually returns. The migration mapping rows are corrected. The agentic entity-shapes guidance notes that an embedded person now carries held-back counts and adetails_url, so an agent can judge whether hydrating is worth a request. The changelog records all of it.Context
The changelog entry also covers a change callers can see: phone
typeandnumberon property owners and residents previously differed from the same fields on the person endpoints, so one phone looked different depending on which endpoint returned it. Both now use one vocabulary and one format. Anyone parsing either field from property responses needs to update, and the entry says so with a before-and-after.The property reference page is regenerated from the published OpenAPI rather than hand-edited, so it tracks the spec.
One thing deliberately left alone: the person migration doc says
result_metadatais exclusive to Person Search. Property uses differently named blocks, so that statement is still accurate.