Today an agent runs six tools by hand between the studio’s delivery and a live listing: correct the images, size them, write the copy, build the site, build the flyer, queue the emails. Each one works. The seam between them is the work, and it is the part that gets skipped at 9pm on a Tuesday. This specifies removing the seams: one input, one approval, everything else generated.
The one input
What the agent actually does.
What comes out
Seven artifacts, from one drop.
Processed image set
Every frame corrected, oriented, and cut to every size the other six outputs need.
Written copy
Narrative sections grounded in the actual photos, plus a short-form description and email subject lines.
Property website
Published, editable in the browser, with its own short URL.
One-page flyer
Print-ready and screen-ready, sharing the site’s hero and price.
Rendered PDFs
Site and flyer, re-rendered from the published HTML.
Email series, queued
Four campaigns, merge fields filled, left as drafts.
A review page
One URL showing all of the above for approval.
The pipeline
Ten stages. Two of them involve a human, on purpose.
Ingest the drop
One input: the studio’s delivery as it arrives — a ZIP, a folder, or a Drive/Dropbox share link. Mixed contents are expected and sorted automatically: high-res stills, HEIC from a phone, floor-plan PDFs, drone frames, video, and the studio’s own contact sheet. Nothing is renamed by hand.
new — ingest_package.pyIdentify the property, once
Address resolved from the folder name, the sidecar, or EXIF GPS, then matched to a live ListingId. The agent confirms the address on screen before anything is generated — the one human checkpoint at the front, because a wrong address poisons all seven outputs.
resolve_address() already exists in generate_listing_site.pyNormalize every frame
EXIF orientation applied and stripped, colour and exposure auto-correction, horizon leveling, and HEIC/PNG converted to a working master. This is the server-side twin of the in-browser Image Auto-Correct, so it can run headless on 60 frames without anyone sitting there.
port of public/image-autocorrect.htmlCrop and fit to the ladder
Each master is cut once per destination — hero, gallery, flyer print, email, social — at the aspect ratios in the table below, with a safe-margin crop that never trims into the frame edge furniture. Print derivatives keep 300 dpi; web derivatives get a WebP plus a JPEG fallback under a size budget.
new — crop_ladder.pySequence and pick the hero
Order follows the studio’s own delivery order, which is already shot in walk-through sequence; the hero defaults to the first exterior front. No automatic room labelling — photo room labels are off across this project pending training data and are not being re-enabled here. The agent can drag to reorder in the review step.
see: photo room labels OFFWrite the copy
Gemini 2.5 Flash reads the actual processed photos plus the MLS facts and returns three to six grounded narrative sections, a short-form description for the flyer, and email subject lines. Grounding rule is already enforced: only what is visible in the photos or stated in the facts. MLS Public Remarks are carried verbatim and never rewritten.
ai_rich_content() / ai_listing_copy() in generate_listing_site.pyBuild the artifacts
Property website, one-page flyer, the standalone bundle, the editor pair, and the rendered PDFs — produced from the same manifest so the flyer and the site cannot disagree about which photo is the hero or what the price is.
generate_listing_site.py · build_standalone.py · _build_editable.pyQueue the email series
The four campaigns — New Listing Outreach, Showing Confirmed, Showing Follow-up, Open House Invite — rendered with merge fields already filled from the listing and the new site URL, and left as drafts. The pipeline never sends on its own.
listing_emails.pyPublish, sync, and gate
One command propagates to all eight places the content lives — canonical file, both Hosting sites, both editor copies, PDFs, bundle, derived flyer copy, short URLs, VERSION stamp — then runs the 52-check verification gate. Exit 0 is the evidence, not a clean upload.
sync_property.py --apply · verify_property.pyHand back one review page
A single URL listing every artifact with a thumbnail, the copy for approval, and the four email drafts. Nothing is client-facing until the agent approves it there, and a short link is minted only on approval.
new — /package/<slug> review viewCrop ladder
One master frame, eight derivatives. Proposed sizes.
| Destination | Aspect | Pixels | Format | Weight |
|---|---|---|---|---|
| Site hero | 16:9 | 2400 × 1350 | WebP + JPEG | ≤ 320 KB |
| Site gallery | 3:2 | 1600 × 1067 | WebP + JPEG | ≤ 180 KB |
| Flyer — print | 4:3 | 300 dpi at placed size | JPEG | no budget |
| Flyer — screen | 4:3 | 1200 × 900 | JPEG | ≤ 140 KB |
| Email header | 2:1 | 1200 × 600 | JPEG | ≤ 100 KB |
| Social feed | 1:1 | 1080 × 1080 | JPEG | ≤ 140 KB |
| Social vertical | 4:5 | 1080 × 1350 | JPEG | ≤ 160 KB |
| Video poster | 16:9 | 1280 × 720 | JPEG | ≤ 90 KB |
What exists, what has to be built
Most of the back half is already running in production.
| build | Package ingest — ZIP / folder / share-link, mixed media sorted |
| build | Server-side auto-correct (the headless twin of Image Auto-Correct) |
| build | Crop ladder and derivative writer, one master to eight destinations |
| build | Asset manifest the site, flyer and emails all read from |
| build | Review-and-approve page, and the short link minted on approval |
| exists | MLS fetch and address resolution by ListingId or free-text address |
| exists | Multimodal AI copy grounded in the actual photos |
| exists | Property website generation, two house styles |
| exists | Flyer generation and browser editing |
| exists | Standalone bundle, editor pair, PDF rendering |
| exists | Four-template email series with merge fields |
| exists | Eight-copy sync and the 52-check publish gate |
Five new pieces, seven existing ones wired differently. The scope is the front half of the pipeline and the review page — not a rewrite of anything that works today.
Guardrails
The rules the pipeline inherits, and does not get to relax.
- Approval before anything is client-facing. The pipeline produces, the agent approves. No auto-send, no auto-share, and the short link is not minted until approval.
- Public Remarks stay verbatim. Generated marketing copy is additive; the MLS remarks are reproduced exactly, misspellings intact.
- No automatic room labelling. Photo labels are off project-wide pending training data; sequencing is delivery-order plus a drag-to-reorder, not a guess.
- Originals are never overwritten. The studio’s masters are archived untouched; every derivative is written beside them, so a bad crop ladder can be re-run.
- The gate decides, not the upload. A copy that cannot be updated is named in the report rather than passing silently.
Phasing
Each phase ends at something demonstrable.
Phase 1 — the front half
Ingest, normalize, crop ladder, manifest. Ends when a drop from the studio produces a complete, correctly sized asset set and nothing downstream has changed yet.
Phase 2 — wire it through
Point the existing site, flyer and email generators at the manifest instead of MLS-hosted photos. Ends when one command turns a drop into a published site and flyer through the normal sync and gate.
Phase 3 — the review page
The single hand-back URL, drag-to-reorder, copy approval, email drafts, short link on approval. Ends when the agent’s whole involvement is: drop the folder, confirm the address, approve.