← back to Realestate Flyers

PLAN.md

110 lines

# RE Flyer Aggregation — PLAN (TK-10708)

**Owner:** re-flyers · **Project:** realestate · **Status:** planning + increment-1 shipped
**Home repo:** `~/Projects/realestate-flyers` (new — flyers span 3+ builds, no single existing home)

---

## 1. Problem / goal

Aggregate real-estate flyers across the RE builds — **usre** (nationalrealestate), **CRCP**
(commercialrealestate), **RENTV** (rentv-bloom-flyers), and HomesOnSpec. Today "flyers" exist
as finished artifacts in exactly ONE place; the other builds hold the *data* that flyers should
be made from but render none. There is no unified index, no shared template, no way to answer
"what flyers do we have / could we have across the RE portfolio?"

## 2. What exists today (investigation findings, verified locally)

| Build | Path | Flyer situation |
|---|---|---|
| **RENTV** | `~/Projects/rentv-bloom-flyers` | **6 finished flyers** — HTML + PDF, US-Letter 816×1056, media-kit concepts (editorial / data-terminal / ad-sales / reach / podcast / CEO). Live at `rentv.agentabrams.com/826`. This is the **reference template + style system** (`:root` palette, `@page letter`, print CSS, inline-SVG logo). |
| **usre** | `~/Projects/nationalrealestate` | **3,198 closed commercial deals** in the `recent_commercial_deals` PG view (`psql -h /tmp usre`) + a 56,492-row `public/properties/property-index.json` (built by re-usre's `ingest-usre-parcels.mjs`, TK-10708 sibling). **Zero flyers rendered.** |
| **CRCP** | `~/Projects/commercialrealestate` | 11 active `buildout-listings` + 6,000 `closed-sales` + broker-of-record data. **Zero flyers rendered.** Built for Frank the loan officer. |
| **HomesOnSpec** | `~/Projects/homesonspec` | Spec-home inventory lives in Kamatera prod DB / admin app, not a local JSON. **Zero flyers; source not yet locally wired.** |

**Net:** 6 rendered flyers vs **9,209 flyer candidates** across sources = **0.065% coverage.**

## 3. What "aggregation" should produce

Two layers, in order:

1. **A unified flyer INDEX/manifest** (increment 1, shipped) — one `flyer-index.json` that lists
   every rendered flyer AND registers every flyer-able source dataset with its candidate count.
   This is the spine everything else reads. Answers "what do we have vs what could we have."
2. **A per-deal flyer GENERATOR** (later increments) — takes a deal/listing row from any source
   and renders a property-spotlight / deal-recap flyer using the RENTV template system, then
   registers the output back into the index. Plus a browse UI (sort + density per Steve's grid
   rule) over the index.

The index is a feed, the generator is the factory, the UI is the storefront — the index comes
first because both the factory and the storefront read it.

## 4. Architecture

```
  usre PG view ─┐
  CRCP JSON  ───┤─► index-flyers.mjs ─► data/flyer-index.json ─┬─► [gen] flyer-from-deal.mjs ─► public/flyers/*.{html,pdf}
  RENTV concepts┤       (increment 1)      (the spine)         └─► [ui]  public/index.html  (grid: sort + density)
  HomesOnSpec ──┘
```

- **Template system:** lift the RENTV `:root` tokens + `@page letter` + print CSS from
  `rentv-bloom-flyers/public/concepts/concept-1` into a shared `templates/property-spotlight.html`.
- **Dedupe:** usre and CRCP closed-deals overlap → dedupe by `canon(address)+'|'+city`
  (re-usre's verbatim key — reuse it, do not invent a second key).
- **De-scoped by design (increment 1):** does not render flyers, does not scrape, does not deploy.

## 5. Data sources per build (canonical refs)

- **usre:** `recent_commercial_deals` view (`psql -h /tmp usre`) + `public/properties/property-index.json`.
- **CRCP:** `data/buildout-listings.json`, `data/closed-sales.json`, broker-of-record sqlite.
- **RENTV:** `public/concepts/*.html` + `public/pdf/*.pdf` (rendered artifacts, the template).
- **HomesOnSpec:** Kamatera prod DB / admin app (later — not local).

## 6. The minimal first increment ("start") — SHIPPED (index + one proven flyer)

Two pieces, both shipped, both $0 / read-only / local / idempotent:

1. **`scripts/index-flyers.mjs` → `data/flyer-index.json`** — the spine. Scans all four builds,
   records rendered flyers + 4 sources + candidate counts (GROSS 9,209 / DEDUP-EST 6,011 —
   honest about the usre↔CRCP closed-deal overlap, per Cody hole #2), computes coverage %.
   Every later piece (generator, UI, coverage canary) reads this one file.
2. **`scripts/flyer-from-deal.mjs` → `public/flyers/*.html`** — *the risk-retiring proof* (Cody's
   top fix). Renders ONE real property-spotlight flyer from the top usre deal
   (**1111 Brickell Ave, Miami — $274.35M office**) using the RENTV brand token system.
   Validates the whole template→data path end-to-end AND the index schema in one pass.
   **Compliance-clean: 0 re-hosted images (`grep -c "<img"` = 0), link-OUT block only, loan-officer
   angle for Frank.** The generated flyer is auto-registered back into the index (`status: rendered`).

Why this is the start (not just the index): the index alone was bookkeeping until a real
artifact proved the template holds variable deal data with no listing photos. Now it does.

## 7. Dependencies

- usre PG (`psql -h /tmp usre`) reachable for the deal count (degrades gracefully to `null` if not).
- re-usre's `ingest-usre-parcels.mjs` property-index (TK-10708 sibling — already committed 8d7215d,
  wired into refine-loop.sh at 9219168). Consumed read-only; no conflict.
- Later: a headless HTML→PDF renderer (the RENTV flyers already use one; reuse that path).

## 8. Compliance rails (hard — from the re-props skill)

- **Sources = public records + press releases ONLY.**
- **Link OUT** to broker/agent/firm/listing (📞 tel · 🏢 firm · 👤 agent · 🔗 listing). **Never
  re-host** their photos/descriptions/content — link, never rip.
- Any RENTV/CRCP/usre **publish or deploy is customer-facing → GATED** to
  `~/.claude/yolo-queue/pending-approval/`. Increment 1 publishes nothing.

## 9. What is GATED (draft, do not auto-run)

- Deploying the browse UI or any generated flyer to a public host (Kamatera / rentv.agentabrams.com).
- Any send-to-list use of a flyer (email blast) — hard-gated, never auto.
- Rendering flyers that embed third-party listing imagery (compliance review first).

## 10. Roadmap (after increment 1)

1. **Increment 2:** `flyer-from-deal.mjs` — render ONE property-spotlight flyer from a usre deal
   row using the shared template (local HTML only, no deploy). Register it back into the index.
2. **Increment 3:** batch-render top-N deals; add browse UI over the index (sort + density).
3. **Increment 4:** wire HomesOnSpec source; add a coverage canary (rendered vs candidates).
4. **Increment 5:** gated deploy memo for the browse UI.