fix: stop publishing aged-out listings and leaking verification vocabulary - #9
Merged
Merged
Conversation
A live tenant's public site showed a card labelled "Listing hidden". The query filter was not at fault — it correctly requires isPubliclyVisible and a VERIFIED/STALE status. Those columns are STORED values, though, written only when a property is mutated, and nothing ages them: updateVerificationState runs on mutations and in the presentation layer, and there is no sweep job. The presented status, by contrast, is recomputed from lastVerifiedAt on every render. So once a listing crossed the hide threshold it kept its stored "publicly visible" flag, stayed on the site, and rendered the internal label "Listing hidden" — the policy was enforced in UI copy but not in the query. The homepage promises "no stale or hidden inventory" directly above it. The public entry points (listing, detail, brochure) now pass a cutoff of now - hideDays, using the company's own thresholds, so the same rule the presentation applies is enforced in SQL. Stored flags are still honoured, so manual hiding keeps working, and the cutoff is optional so non-public callers are unaffected. Consequence worth knowing: a listing that has aged out disappears from the public site until someone re-verifies it. That is the intended policy — it is what the "Listing hidden" label already claimed was happening. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The public property card rendered a "Stale" badge for anything not VERIFIED and printed property.verification.label verbatim, which produces operator strings such as "Listing hidden" and "Verification required" in front of buyers. On a site whose pitch is verified listings, that reads as a warning about the company rather than about the listing. Only the positive state is public now: a "Verified" badge and its label when the listing is verified, and nothing otherwise. Absence is quieter than a warning, and the internal workflow stays internal. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Regression I introduced in the mobile touch-target pass: the footer links were changed from `block` to `inline-flex` to get a 44px tap target, but their container still separated children with `space-y-2`, which is a margin between BLOCK-level boxes. Inline-level boxes flow onto one line instead, so the footer rendered "ListingsBuyer PortalAdmin Dashboard" and "AboutTeamCareersContact" at every width. The containers are now flex columns, so each link starts its own line while keeping inline-flex + min-h-11 and the 44px target. The E2E sweep now asserts this per column (separate columns legitimately share a line on desktop) and checks the 44px height on phone viewports only, since above `sm` the links deliberately return to desktop density. Verified the test fails against the old markup and passes against the fix. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
This was referenced Sep 18, 2026
This branch was successfully deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Correctness pass from the tenant public-site audit. The homepage restructure is deliberately not here — it is layout work, whereas this changes what is publicly visible and should be reviewable (and revertable) on its own.
1. Aged-out listings stayed public — the real bug
A live tenant's site showed a property card reading "Listing hidden", publicly.
The query filter is not at fault: it requires
isPubliclyVisible: trueand aVERIFIED/STALEstatus. But those are stored columns, written only when a property is mutated —updateVerificationStateruns on mutations and in the presentation layer, and there is no sweep job that ages them.The presented status is recomputed from
lastVerifiedAton every render. So a listing that crossed the hide threshold kept its stored "visible" flag, stayed on the site, and rendered the internal label. The policy was enforced in UI copy and not in the query — while the homepage promises "no stale or hidden inventory" directly above it.The public entry points (listing, detail, brochure) now pass a cutoff of
now - hideDays, taken from the company's own thresholds, so the rule the presentation applies is enforced in SQL. Stored flags are still honoured, so manual hiding still works; the cutoff parameter is optional, so non-public callers are unchanged.Consequence worth knowing before merge: an aged-out listing leaves the public site until someone re-verifies it. For the audited tenant that is its only listing, so its listings page will be empty until it is re-verified. That is the intended policy — it is what the "Listing hidden" label already claimed was happening.
2. Internal vocabulary reaching buyers
The card rendered a "Stale" badge for anything not
VERIFIEDand printedverification.labelverbatim ("Listing hidden", "Verification required"). Only the positive state is public now: a "Verified" badge and label when verified, nothing otherwise.3. Footer links ran together — my regression
From the mobile touch-target pass: links moved from
blocktoinline-flexfor a 44px target, but their container still usedspace-y-2, a margin between block-level boxes. Inline-level boxes flow onto one line, so the footer renderedListingsBuyer PortalAdmin Dashboardat every width. Containers are flex columns now, keepinginline-flex+min-h-11.Regression test added and verified both directions: it fails against the old markup ("footer links in the same column share a line") and passes against the fix. It compares links per column, since separate columns legitimately share a line on desktop, and asserts the 44px height on phone viewports only — above
smthe links deliberately return to desktop density.Verification
npm run check🤖 Generated with Claude Code