WiseAI Realtor — Listings Expected Output Spec
⛔ STATUS: DRAFT — NOT APPROVED. CLAUDE.md Rule #17 (HARD GATE) is NOT satisfied.
Stage-1 agent research, pre-populated from the realtor design docs + the
listings.html+studio.htmlmocks. Do not build the customer-facing screen until the founder approves and confirms the open items in a Stage-2 interview.
Sourced from (read-only, 2026-06-29):
do-not-reinvent.md(the shipped DDF sync + ListingStudio + REListing types + the "edit=manual / feature+reorder=all" BEAT + the SellingToolz duplication trap + the listings/REALTOR.ca direction + trademark rules),data-model.md§1.5/§1.6 (local_business_listingsKEEP +re_listing_overlayNEW),backend-completeness-audit.md(media data model; "coming soon"/exclusive board rules),ai-guardrails.mdRULE 3 (facts-used / no fabricated listing facts), andrealtor-mockups/{listings.html,studio.html}.
0. Scope — what this spec covers, and what it does NOT
This spec covers the Listings manager (card gallery / table / map, filter
bar, status, the featured + reorder overlay, listing media) and the listing
side of Content Studio (the AI Listing Kit — MLS copy ×3, fair-housing
scrub, social/email, variants). Sibling of realtor-qr-campaigns-mvp.md (a QR
campaign connects to a listing) and realtor-analytics-mvp.md (per-listing
leads/DOM).
Does NOT cover (cross-referenced, not duplicated):
- The public listing-detail page + search + scan landing — already shipped
(
/s/[slug]/listing,/scan;do-not-reinvent.md). - The broader Content Studio (blog / newsletter / buyer-guide / market-update
/ per-language content generation) — the listing-copy mode of
studio.htmlis covered here (it IS the Listing Kit); the multi-type content side is covered by the already-approvedacceptance/real-estate-content-editor-mvp.mdreal-estate-blog-mvp.md, and per-language generation is partly greenfield (do-not-reinvent.md"Genuinely greenfield"). Cross-reference those; do not re-spec them here.
- The DDF sync engine itself (LIVE, PR #1064 —
ddf/sync.tsis manual-safe; do NOT rebuild). This spec governs the manager UI over it.
Build posture: Listing Studio, DDF sync, the REListing shape, and the public
listing pages already shipped (do-not-reinvent.md). This screen extends
them with the manager UI + the re_listing_overlay (featured/sort) — the one
genuinely new piece. Extend, do not rebuild.
1. The mock screens this spec governs + what they read
Governs realtor-mockups/listings.html: a card gallery (Gallery / Table
/ Map toggle) with a header ("8 active · 2 manual · 6 from MLS") + a CREA DDF ·
sample feed pill, a filter bar (location/address/MLS#, min/max price, beds,
baths, type, status Active/Pending/Sold), and cards showing photo, status badge
(Active/Pending/Sold gold-celebration), a source badge (Manual vs MLS),
price, beds/baths/sqft, DOM + leads, and a Listing kit ✦ affordance on
manual cards. (Table view adds sortable columns: price, beds, DOM, leads.)
Governs realtor-mockups/studio.html (listing-copy mode = the Listing Kit): a
3-pane editor — brief (content type incl. Listing copy, the selected listing,
tone, languages, key facts used verbatim) · editor (the generated MLS
description) · AI assistant (quick commands: Shorten / More luxury / Add CTA /
Translate → Punjabi / Make social post / Email-to-database; a Fair-housing
check passed card; A/B/C variants; a social preview).
Reads/writes (verify exact route names; do not fabricate):
| Surface | Underlying store |
|---|---|
| Card gallery / table / map | local_business_listings (price/status/beds/baths/sqft/photos/maps; source manual|ddf, ddf_id) via src/lib/real-estate/server/listings.ts |
| Source badge (Manual vs MLS) | local_business_listings.source |
| Featured + sort order | re_listing_overlay (NEW — featured flag + sort_order keyed by listing_id/mls_number/ddf_id; §4.2) |
| DOM + leads per listing | listing dates + re_interactions/re_contacts attributed to the listing |
| Listing Kit (MLS copy ×3, fair-housing scrub, social/email) | ListingStudio.tsx + src/lib/real-estate/listing-kit.ts + fair-housing.ts |
| Translate → language | per-language generation (greenfield; reuse chat multilingual path) |
Demo tenant: Terry & Sheri Real Estate (…0c01, terry-and-sheri) —
Beckett c01 has 11 real own DDF listings (office 296475) + manual additions
that coexist and survive sync (do-not-reinvent.md). NEVER a real customer
(feedback_never_modify_customer_data).
2. The AI-Bridge / honesty anchor
- No fabricated listing facts. The Listing Kit generates copy from the
key facts used verbatim + the live
local_business_listingsrow — it never invents a feature, price, or status (ai-guardrails.mdRULE 3; the manual vs MLS honesty rule). - Fair-housing scrub on generated copy. Every generated listing description
passes the fair-housing linter (
fair-housing.ts) — "describes the home, not the buyer", Ontario Human Rights Code safe — surfaced as a visible check. Copy that fails is flagged, not silently published.
3. Role-based visibility
| Bucket | Roles | Listings scope |
|---|---|---|
| Brokerage management | brokerage_owner, broker_admin (brokerage scope) | All listings (brokerage) + featuring/order + Listing Kit |
| Team management | team_admin (team scope) | All listings within the team + featuring/order + Listing Kit |
| Agent / ISA | agent, isa (own scope) | Own manual listings (edit) + feature/reorder per policy; team feed listings visible |
| Support / external | transaction_coordinator, marketing_assistant, external_partner (bounded) | View / scoped; marketing_assistant may run Listing Kit per policy; external_partner read-only |
4. Expected outputs
4.1 — Listings gallery / table / map + source + status + DOM/leads
Should see:
- A Gallery / Table / Map toggle over a unified set of manual + DDF listings (the CREA DDF sample feed + the agent's exclusives in one view), each card with a status badge (Active / Pending / Sold = gold celebration), a source badge (Manual vs MLS), price, beds/baths/sqft, and DOM + leads.
- A filter bar (location/address/MLS#, price, beds, baths, type, status). Table view = sortable columns (price/beds/DOM/leads — the founder sortable-header rule, persisted per view). Map view plots listings.
Should NOT see:
- Manual and DDF listings in separate silos (the value is a unified manager).
- A DDF (
source='ddf') row presented as editable MLS data (§4.3).
Success: the agent manages every listing — own exclusives + MLS feed — in one place, with source/status/DOM/leads legible per card.
4.2 — THE BEAT: edit = manual only; feature + reorder = ALL (via overlay)
This is the differentiator that beats SellingToolz (
do-not-reinvent.md).
Should see:
- Editing listing facts (price/beds/description/photos) is allowed for MANUAL listings only — DDF/feed listings are read-only (you can't edit the board's authoritative record).
- Featuring AND drag-reordering works on ALL listings — feed (DDF) AND
manual — via the
re_listing_overlay(afeaturedflag +sort_orderkeyed by listing_id/mls_number/ddf_id). The homepage "Featured/Scroll" carousel rendersfeatured DESC, sort_order ASC. No editing of MLS data; no duplication. - The overlay is decoupled from the feed: the 30-minute DDF
sync.tsupserts feed rows but never touches the overlay, so an agent's featuring/order survives every sync (the silent-reset regression the on-row approach risks —data-model.md§1.6).
Should NOT see:
- A DDF listing made editable, or an agent forced to re-create (duplicate) a feed listing to feature/reorder it (the SellingToolz trap we explicitly beat).
- Featuring/order silently reset after a DDF sync.
Success: an agent features + drags a DDF listing to the top of their carousel; a sync runs; the featuring/order persists and the MLS data is untouched (verify across a sync cycle — behaviour at ≥2 timepoints).
4.3 — MLS®/REALTOR® trademark + manual = pre-market (compliance)
Should see:
- Trademark correctness only on real MLS listings: MLS#/REALTOR.ca references,
"Powered by REALTOR.ca", and "courtesy of [listing brokerage]" attribution
attach only to
source='ddf'rows. A manual listing carries no MLS#, is not on REALTOR.ca, and is labelled pre-market / coming-soon / exclusive. - A "coming soon" manual listing surfaces the board exclusive-listing rule (CREA cooperation: on MLS within ~3 days of public marketing; PropTx requires immediate showing availability) — the UI must not design features that encourage off-MLS marketing.
Should NOT see:
- A manual/coming-soon listing implying it is on REALTOR.ca or carrying an MLS#.
- MLS®/REALTOR® trademarks in a page title/meta/slug/domain (that's the website
compliance gate —
realtor-website-builder-mvp.md).
Success: trademark + attribution appear only where legitimate; manual listings are honestly pre-market with the exclusive-rule reminder.
4.4 — Listing media (gallery order, video/3D/floorplan, alt-text, storage)
Should see:
- A listing media manager: gallery photo ordering (drag), support for video / 3D tour / floorplan assets, alt-text per image (accessibility + AI honesty), and storage/upload handling. (Audit P1 listing-media gap.)
- For DDF listings, media comes inline from the feed (the live feed returns
Media inline;
$expand=Mediais invalid —do-not-reinvent.md); the manager surfaces feed media read-only and lets the agent add alt-text/overlay without editing the MLS record.
Should NOT see:
- Media editing that mutates the DDF authoritative record.
- Images without an alt-text affordance (AODA).
Success: the agent orders the gallery, adds video/floorplan + alt-text, and it renders on the public listing page.
4.5 — Listing Kit (AI MLS copy ×3 + fair-housing scrub + social/email + translate)
Should see (studio.html listing-copy mode):
- The Listing Kit: AI MLS description in 3 lengths/variants (A warm / B punchy / C luxury) from the key facts used verbatim, a visible fair-housing check result, quick commands (Shorten / More luxury / Add CTA / Translate → Punjabi / Make social post / Email-to-database), and a social preview.
- Generated copy is editable and draft → publish to the manual listing / social channels.
Should NOT see:
- Generated copy that fabricates a fact not in the key facts / listing row
(
ai-guardrails.mdRULE 3). - Copy that fails the fair-housing scrub being publishable without a flag.
- ⚠️ Carried-forward (minor): the Listing Kit must NOT generate a CTA that
implies the AI books a showing ("book your showing today" as agent copy is
fine; an AI auto-booking is not) — showings are captured requests
(
ai-front-desk.mddecision 4).
Success: the agent generates compliant, fact-grounded MLS copy in 3 variants + a translation + a social post, all passing the fair-housing scrub.
5. Empty / loading / error states
| State | Expected output |
|---|---|
| No listings | Demo-seeded preview + "Add your first listing / connect DDF" CTA — never a blank gallery (anti-pattern #6). |
| DDF not authorized / feed down | Honest "MLS feed not connected / temporarily unavailable" (non-destructive — manual listings still render); never a fake feed (mock labels its feed "sample"). |
| Loading | Card/table skeletons, not a spinner (anti-pattern #7). |
| Listing Kit generation error | Inline "Couldn't generate — retry"; the draft + key facts preserved. |
| Fair-housing fail | The generated copy is flagged with the offending phrase; publish is gated until resolved. |
| Media upload error | Inline retry; other media intact. |
6. Carried-forward constraints (consistent across the batch)
- Aria does NOT book/schedule — Listing Kit CTAs frame showings as agent-handled / captured requests, never AI bookings (§4.5).
- SMS off-by-default + consent-gated; crisis/safety never gated (a listing chat that surfaces a crisis still routes to 988).
- Role enum (rbac.ts — implemented source of truth): agent | team_admin | transaction_coordinator | broker_admin | brokerage_owner | marketing_assistant | isa | external_partner (scopes own|team|brokerage).
- No realtor pricing — gate by role/module.
- Honest metrics + no fabricated facts; language never hardcoded (translate
targets = the agent's enabled languages; non-EN copy needs native review per
ai-guardrails.md). - Reuse existing embed/infra, not a new cdn domain.
7. Accessibility (AODA → WCAG 2.1 AA)
- Gallery/table/map: keyboard-navigable; cards are links/buttons with
accessible names; Table uses real semantics +
aria-sort; status/source badges convey by text, not color alone (Sold gold is celebration accent + label). - Media: alt-text is first-class (required affordance per image); video has controls + captions affordance; drag-reorder has a keyboard alternative.
- Listing Kit: editor + variant cards + commands keyboard-operable; the fair-housing result is text (not color-only).
- Contrast ≥4.5:1; gold accent only; teal actions meet AA; reduced-motion honored.
8. Acceptance checklist (QA runs on the deployed URL)
Behavioural verification on wiseaiagency.com (real host) against the demo
tenant (…0c01); never "build passes". Sample at ≥2 timepoints; assert change.
// Manager + source/status
test.fixme('Gallery/Table/Map show manual + DDF unified; cards show source (Manual/MLS) + status + DOM + leads', () => {});
test.fixme('Table view sorts price/beds/DOM/leads (persisted per view); filters narrow the set', () => {});
// THE BEAT
test.fixme('editing listing facts is allowed for MANUAL only; DDF/feed rows are read-only', () => {});
test.fixme('feature + drag-reorder works on ALL listings (DDF + manual) via re_listing_overlay — no duplication', () => {});
test.fixme('featuring/order survives a DDF sync cycle (overlay decoupled; MLS data untouched)', () => {});
// Trademark + manual = pre-market
test.fixme('MLS#/REALTOR.ca/"courtesy of" attach ONLY to source=ddf rows; manual = no MLS#, pre-market/coming-soon', () => {});
test.fixme('a "coming soon" manual listing surfaces the board exclusive-rule reminder; no off-MLS-marketing nudge', () => {});
// Media + Listing Kit
test.fixme('media manager orders gallery + supports video/3D/floorplan + REQUIRES alt-text; DDF media stays read-only', () => {});
test.fixme('Listing Kit generates 3 fact-grounded variants + translate + social, all passing the fair-housing scrub', () => {});
test.fixme('generated copy fabricates NO fact beyond key facts/listing row; a fair-housing fail gates publish', () => {});
// States + a11y
test.fixme('empty/DDF-down/loading/error/fair-housing-fail states render per §5; never blank, never a spinner, no fake feed', () => {});
test.fixme('gallery/table keyboard-navigable; alt-text first-class; badges not color-only; reduced-motion honored', () => {});
9. Guardrails for agents building against this spec
- Rule #17 not satisfied — do not build until founder approval.
- Extend the shipped Listing Studio + DDF sync + REListing types — do NOT
rebuild (
do-not-reinvent.md);ddf/sync.tsis manual-safe and LIVE. - THE BEAT lives in
re_listing_overlay— feature/sort decoupled from the feed so sync never clobbers it; edit=manual, feature+reorder=all; never duplicate. Migrate-before-use (data-model.md§1.6, Rule #18); founder-gated DDL. - Trademark only on real MLS rows; manual = pre-market, no MLS#, board exclusive rules (audit compliance must-haves).
- No fabricated facts; fair-housing scrub gates publish; Aria books nothing; language never hardcoded; honest metrics; verify on the real host; evidence-or-nothing.
- If code diverges, update the spec first (founder approval), then the code.
End of spec. STATUS: DRAFT — NOT APPROVED. Open items for Stage-2: (1) cut over
featured/sort_order to re_listing_overlay now vs special-case the DDF upsert
(data-model.md §1.6 recommends the overlay); (2) media storage backend +
video/3D providers; (3) which Listing-Kit languages ship + native-review gating;
(4) how DOM/active-vs-total counts reconcile (Beckett showed 11 board vs 9 scroll).