← 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.