FLYERS

Living product documentation

Flyer generation wiki

A source-linked explanation of every component that turns a submitted request into an editable CoreFirms brochure and PDF.

Pipeline stages
12
Instruction sources
11
Live instructions
12
Source fingerprint
bc8873a5fd327875ccb5
How this wiki stays current

Automatic on every build

  • Every production build scans the prompt-bearing flyer source files.
  • The live instruction catalog, source hashes, line counts, and instruction counts regenerate automatically.
  • Changing an extracted prompt or project guardrail changes the wiki source fingerprint without hand-editing the page.

Required with behavior changes

  • Update the matching stage above when the pipeline behavior, fallback, input, or output changes.
  • Keep every field/item source entry accurate when its authoritative form field, provider, derivation, or editor override changes.
  • Update the page-by-page source map whenever a visible flyer element, source precedence, editor override, fallback, or page-inclusion rule changes.
  • Add new prompt-bearing files to the wiki snapshot generator.
  • Run npm run refresh-wiki before review; build and typecheck also run it.

End-to-end flow

From request to delivered PDF

  1. 1Request intake
  2. 2Data resolution
  3. 3Template + content model
  4. 4Photos + map + copy
  5. 5Editable preview
  6. 6Print layout
  7. 7PDF + delivery

Page-by-page source map

Where every visible flyer item comes from

Read each row as: flyer item → authoritative or generated source → reviewer override and fallback order.

Page 1

Cover

Always included.

Flyer itemPulled fromOverride / fallback order
Cover photoProperty- and Listing-scoped uploaded image assets plus brochure-only pictures added in the preview. The model uses the asset chosen as photoSelection.coverAssetId / primaryImageUrl.A reviewer can promote any supporting photo to the cover. The former cover takes the promoted photo's Page 2 or gallery position, and coverPhotoAssetId plus supportingPhotoAssetIds persist the swap in the editor draft and PDF. Browser and server draft restoration repair an older or partial saved order that omitted the former cover so it returns to the normal photo lineup. If no chosen cover resolves, the first selected image is used; if no image exists, BROCHURE PHOTO or a blank placeholder is shown.
Cover offer header (FOR LEASE / FOR SALE)Listing Type, normalized to For Lease, For Sale, or For Sale or Lease.textOverrides.offerTitle wins. Otherwise a source fact labeled Deal type, Transaction, or Offering can determine it before the normalized Listing Type fallback.
Cover property-type header/titleProperty Campus Name when populated; otherwise Property Type. A Property Type value accidentally appended to Campus Name is removed from the displayed title.A genuinely custom textOverrides.coverTitle wins after a final parenthetical group is removed. Generated Campus Name and Property Type fallbacks use the same trailing-parenthetical cleanup while preserving parentheses elsewhere in the title. Retail, Industrial, and Office Property Type fallbacks display as Retail Space, Industrial Space, and Office Space without duplicating an existing Space suffix. Older saved generic Retail, Industrial, or Office overrides are upgraded to the matching Space title. Saved generic or unsanitized campus titles are upgraded to the clean Campus Name. Property Type is used only when Campus Name is blank or contains no value beyond Property Type.
Cover addressListing Specific Address and Specific City when present; otherwise Property Address and City, plus Property State and Zip code.A valid textOverrides.coverAddress wins and is recombined with the resolved city/state/ZIP. Common numbered street suffixes, including Trail and Trl., are preserved; invalid or prose-like address overrides are discarded. The standard cover displays the address in a large condensed navy treatment, bolding the street before the first comma while keeping the city/state/ZIP lighter. Its white ribbon sizes to the displayed address and keeps medium-length addresses on one line; only unusually long addresses use a compact wrapped treatment.
Cover contact promptFixed CoreFirms copy: “For More Information, Please Contact:”.textOverrides.contactStrip replaces the fixed copy.
Cover overview bullets (redesign templates)The generated overview, based on Listing Remarks (Public), selected source-backed facts, and supported map context.The first three cleaned overview sentences are displayed. Accepted Page 2/overview edits flow through the edited model.
Cover detail photo (redesign templates)The first selected supporting Listing image that is different from the cover photo.Falls back to the next uploaded image, then to the cover image if no distinct supporting image exists.
Cover CORE logoCore brand assets under public/brand/core.The template supplies the default color; styleOverrides.coreLogoColor can select blue, orange, or white.
Cover/footer websiteCoreFirms brand default: CoreFirms.com.textOverrides.footerWebsite replaces the default.
Cover/footer office addressCoreFirms brand default: 16 Casco Street, Portland, ME 04101.textOverrides.footerAddress replaces the default.
Cover broker nameListing Primary Agent, Secondary Agent, and Tertiary Agent are matched to managed profiles in data/brokers; the profile name is displayed.Reviewer-selected broker profile slugs control the displayed set/order. No unmatched name is invented.
Cover broker title, phone, email, and headshotThe matched managed broker JSON profile and its referenced public/brokers headshot asset.If no profile matches, the footer shows textOverrides.contactHeading/contactText, then Listing agent text, then Broker TBD.

Page 2

Property information

Always included.

Flyer itemPulled fromOverride / fallback order
Page header and CORE logoFixed template label Property Information and the orange Core brand logo.Retail lease layouts omit the standard page header.
Fact-row labelsPublic, nonblank Property and Listing form field names normalized into brochure labels; Address, Offering, Property Type, and Location are generated request facts.The editor and preview use the same Property Information selector. By default Address is first and nonblank Property Type is directly below it. A nonblank Listing List Price is included next for for-sale and sale-or-lease brochures, followed by any single-Listing lease rows and then available physical facts in this order: Lot Size, Building Size, Road Frontage, Year Built, Roof Type, Construction Siding, HVAC, Elevator, Zoning, and Parking Spaces. Industrial clear-height and loading-door rows follow when applicable. Reviewers can move visible rows up or down or delete them from the brochure editor; the saved order and hidden-row keys win in the preview and PDF without clearing the Airtable source value. Reviewer-added rows remain visible, Miscellaneous is last by default, and blank values are omitted. Sanitary Sewer is not selected by default, but its nonblank Airtable field remains available in the Page 2 add-input menu and displays when the reviewer chooses it. Older saved drafts that predate explicit optional-fact tracking drop a formerly automatic Sanitary Sewer row.
Fact-row valuesExact Property/Listing form values. Address uses Listing-specific location first; Offering uses Listing Type; Property Type uses Property Type/Primary Use/Secondary Property Type.Reviewer-edited fact values win. Nonblank List Price is a default Page 2 row sourced from the linked Listing form; aggregate sale brochures use the one shared price or Varies when selected sale Listings have different prices. Duplicate labels are merged, internal/link/contact fields are removed, whole numbers remain whole, and every decimal number displayed in the Property Information table is rounded to exactly two decimal places. Area values use display-only commercial real-estate notation with the marker after the number: square footage renders as {value} +/- Sq. Ft. and acreage as {value} +/- Acres without duplicating an existing approximation marker or unit.
Lease rates and expenses blockAvailable linked Spaces/Units supply each square footage and their linked Listings supply asking rent. Listing Lease Type and Expenses/Expense Type supply the summary rows.A brochure with one lease places ordinary space-size, Lease Rate, Lease Type, and expense fact rows after the default Address and Property Type rows and shows those same rows in the editor as linked-Listing values for review. Reviewers can move these visible rows with the other Property Information rows. The space label preserves its Airtable source title: Available SF or Rentable SF for a Space/Unit record, and Total Available for the Listing fallback. Two or more leases/units use a dedicated Lease Rates block, sort available sizes from largest to smallest, and show each source-backed rate beside its size. Tax & CAMs can label the expense row Est. CAM, but the displayed expense value/type stays exactly source-backed; the template does not append an expense unit or infer that property taxes are included. Missing size/rate pairs are omitted rather than inferred.
Physical property factsProperty Land Size Acres, Building Size, combined Frontage/Frontage Street, Year Built, Roof Type, Construction Type, HVAC, Elevator, Miscellaneous, Zoning, and Total Parking fields.Values retain their Property-form meaning with display-only formatting and brochure labels: decimals use exactly two places, land size displays as {value} +/- Acres, building size and any other square-footage value display as {value} +/- Sq. Ft., Construction Type is labeled Construction Siding, and Total Parking is labeled Parking Spaces. Missing fields are omitted.
Property descriptionFor a single Listing, Listing Remarks (Public) is authoritative. For an aggregate brochure, the linked Property's Description input (stored in Airtable Notes) is authoritative when populated, with the selected Listings' public remarks as the fallback.textOverrides.pageTwoDescription wins. For a single Listing with blank Public Remarks, DeepSeek creates exactly two paragraphs from the resolved address and public Airtable-backed facts; a deterministic two-paragraph version is used only if DeepSeek fails.
Page 2 photoProperty- and Listing-scoped uploaded images plus brochure-only pictures added in the preview; pageTwoPhotoAssetId identifies an explicit reviewer choice and fullPagePhotoAssetIds identifies pictures assigned their own pages.Reviewer-removed pictures are excluded through removedPhotoAssetIds without deleting the original Property or Listing upload. Otherwise this falls back to the first selected supporting image, then the first uploaded image different from the cover, then a PROPERTY PHOTO placeholder.
Page 2 photo captionThe linked Listing name when a combined brochure uses a Listing-scoped image.Omitted for shared Property pictures and ordinary single-Listing brochures.
Page footer/title and numberFixed template label Property Information and computed page number 02.No intake-field override.

Pages 3+

Property photo galleries

Included when supporting photos remain after the Page 2 image is removed from the gallery sequence; four photos per page.

Flyer itemPulled fromOverride / fallback order
Gallery photosProperty- and Listing-scoped uploaded image assets and brochure-only preview uploads selected as supportingPhotoAssetIds, excluding the cover and Page 2 photo.For a combined brochure, shared Property pictures come first, followed by each selected Listing's pictures kept together in selected-Listing order. Reviewer reorder/selection wins after the initial grouping. The editor can remove a supporting picture from only this brochure and restore it later; removedPhotoAssetIds persists that reversible exclusion in preview and PDF without deleting the authoritative Property, Listing, or brochure-upload asset. Promoting a gallery or Page 2 image to the cover swaps the former cover into that same supporting position. With no saved order, every valid uploaded image is included after duplicate URLs and the selected cover are removed. Older saved orders remain first and any valid photos omitted by the former limit are appended automatically. Additional four-photo gallery pages are created until all remaining photos are shown. Gallery tiles preserve the complete image with contained fitting and use the white brochure background for any unused tile area instead of gray bars; cover and Page 2 imagery retain their selected treatment.
Photo orderCombined-brochure Property/Listing source grouping, then deterministic photo scores within each group based on filename, label, source, file size, upload order, and property type.The supportingPhotoAssetIds order stored in FlyerEditorDraftState determines order within each preserved Property/Listing group and the first occurrence of each group. The brochure editor flattens the same Page 2, grouped-gallery, and full-page plan used by the preview, so its top-to-bottom picture order and placement labels match the brochure and PDF.
Photo captionThe authoritative linked Listing title for Listing-scoped pictures in a combined brochure.Shared Property pictures remain uncaptioned; accessibility text falls back to the filename, asset label, or Property photo N.
Page header, CORE logo, footer, and page numberFixed Property Photos template labels/Core logo; page numbers are calculated from the gallery page position.No intake-field override.

Optional page

Market snapshot

Generated and included by default as soon as the saved editor draft finishes loading; it no longer waits for background map research. A reviewer can exclude the generated page, and that explicit choice remains authoritative.

Flyer itemPulled fromOverride / fallback order
Page titleFixed brochure template title: Market Demographics.The fixed title is also used when an older saved snapshot contains a generated title.
Subtitle/trade-area statementProperty municipality and state rendered deterministically as a trade-area label.Falls back to the Property ZIP and then Trade Area; older generated subtitles are not displayed.
Metric labels, values, and detailsU.S. Census ACS responses resolved from the Property ZIP/state/location.Up to six supported metrics are displayed without secondary captions; no unavailable metric is fabricated.
Trend labels, values, bars, and detailsSupported Census time-series metrics; bar strength comes from the sanitized positive trend strength.The graph appears only with at least two valid positive trends and displays up to three. Its visible header is fixed as Demographic Trends, including when an older saved snapshot contains Positive Demographic Trends.
Page inclusionDefault flyer generation enables the page, then the editor automatically requests and saves the source-backed snapshot immediately after its local/server draft finishes loading.An explicit reviewer choice marked by marketSnapshotPagePreferenceSet wins. Older drafts with includeMarketSnapshotPage=false but no preference marker migrate to the current default instead of silently suppressing automatic generation.
Footer/title and page numberFixed Market Demographics footer label; page number is calculated after gallery pages.No intake-field override.

Optional page

Aerial map

Included when map research exists and includeMapPage is not false.

Flyer itemPulled fromOverride / fallback order
Map background imageMapbox static map centered from Property Latitude/Longitude or the geocoded resolved Listing/Property address.Saved map viewport/map image settings win; a neutral map fallback is shown if no image resolves. The initial map viewport is modestly zoomed in relative to the researched static-map zoom. Reviewer panning can center the map away from the property to make room for an uneven cluster of nearby logos.
SITE pinResolved property coordinates from Property Latitude/Longitude or address geocoding.Position is projected into the saved/current viewport and may remain off-center after reviewer panning, with a safe-frame limit that keeps it visible; omitted when no property coordinate exists.
Nearby business/place labelsMapbox and OpenStreetMap/Overpass place candidates scored by category, distance, prominence, and property-type rules.AI may rank only supplied candidates. Refreshed places, hidden names, and maxPlaces saved in map settings control the visible set.
Business logosLogo.dev for recognized place brands.Duplicate rendered brand/logo identities are collapsed to one logo, preferring custom or manually positioned versions. Reviewer-uploaded custom logos can be added; hidden-logo, transparency, scale, position, and size settings win.
Logo leader lines and location dotsThe selected place coordinate projected against the current map viewport.Reviewer can disable leader lines or move logos; lines reconnect the moved logo to its source place.
Numbered map pins/callout listThe ranked visible map places.Controlled by showCalloutList and map availability; map logo overlays remain independently configurable.
AERIAL MAP titleFixed template copy.No intake-field override.
Page inclusionReviewer action in the map/editor settings.includeMapPage in FlyerEditorDraftState wins.

Final page

Closing/contact

Included by the reviewed brochure/PDF route unless the render explicitly suppresses the closing page.

Flyer itemPulled fromOverride / fallback order
Closing property titleThe same resolved cover property-type title used on Page 1.Uses the same clean Campus Name or Property Type fallback as Page 1, keeping the cover and closing page consistent.
Closing property addressThe same resolved Listing-specific/Property address used on Page 1.A valid textOverrides.coverAddress wins; the address is split into street and city/state/ZIP display lines.
Closing CORE logoWhite Core brand asset under public/brand/core.Fixed closing-page treatment.
Closing contact promptFixed CoreFirms copy: “For More Information, Please Contact:”.No intake-field override.
Broker nameListing Primary/Secondary/Tertiary Agent matched to the managed data/brokers profile name.Reviewer-selected broker profile slugs control the set/order.
Broker titleMatched managed broker JSON profile title.Omitted when blank; never inferred from Listing text.
Broker phone and extensionMatched managed broker JSON profile phone and extension.Extension is appended when it is not already present; the line is omitted when phone is blank.
Broker emailMatched managed broker JSON profile email.Omitted when blank.
Broker headshotpublic/brokers asset referenced by the matched managed broker JSON profile.No invented or generic headshot is substituted.
Unmatched-broker fallbackListing agent text assembled from Primary, Secondary, and Tertiary Agent.textOverrides.contactText wins; Broker TBD is shown only when neither override nor Listing agent text exists.
1

Generation component

Request intake and normalization

Use the Airtable Property and Listing forms as the only authoritative data inputs before generation begins.

Inputs

  • One submitted Airtable Listing form record
  • The single Airtable Property form record linked from that Listing

What happens

  • Loads the Listing first, requires its linked Property ID, and then loads that exact Property record.
  • The separate Core Import Lab can prefill these authoritative Airtable Property and linked Listing records from a reviewed Core property URL. It scans every associated Core Listing, attached flyer, and photo without writing anything until the reviewer saves each stage. Explicit facts extracted from the newest relevant Core flyer take precedence over the structured Core feed; an existing nonblank Airtable value is never preselected for overwrite. Core IDs make reimports idempotent, defaulting to blank-field fills, and unsupported facts appear as mapping gaps rather than creating schema automatically. Listing saves isolate Airtable-controlled dropdown fields: compatible choices are saved, while a stale or unsupported choice is skipped without blocking the rest of the Listing and is named in the completion notice.
  • Core's structured utility, rail-service, and grade-level-door availability booleans are treated as affirmative-only evidence: true proposes Yes, while false remains blank because Core's public listing does not distinguish an unadvertised/default false from an explicit No. A flyer can still supply an explicit Yes or No and remains the preferred source.
  • Core Import agent matching uses the active Airtable Team display name and treats Charles Hippler and Charlie Hippler as the same broker identity, preserving whichever display name is stored in Airtable.
  • For a combined flyer, the user selects one or more Listings linked to the same Property; generation rejects cross-Property selections and groups available Spaces/Units by category.
  • The Listing supplies offering-specific terms, status, public remarks, dates, agents, pricing/rent, availability, and internal review context.
  • The Property supplies the canonical address, property type/use, building/site details, utilities, location, and physical facts.
  • Property add/edit forms omit Parcel IDs, Cross Street, Center/Park Name, Opportunity Zone, Latitude, Longitude, Current Uses, Investment, Is Vacant and its Vacant Date, Owner Occupied, Condo and its fee description, Parking Ratio, Topography Description, Restrooms, Offices, Yard, and Water Rights. Existing Airtable values remain readable and are not cleared by the intake.
  • Property Information includes a full-width Description textarea backed by Airtable's existing Notes field. A visible note asks users to complete it for aggregate brochures; leaving it blank does not make an otherwise complete Property fail completeness review.
  • Existing-property search matches each meaningful query term across the Airtable Property ID, name, address, city, state, and ZIP fields, so natural searches such as '72 Main St in Livermore Falls' distinguish same-number street addresses in different cities.
  • The Property intake presents Primary Use and Secondary Use as controlled single-choice questions, including Warehouse, while storing them in Airtable's live text-compatible Property columns. Warehouse is treated as industrial for conditional Property and Listing sections and brochure normalization. Save normalization is table-specific: Property, Listing, Space/Unit, and Rent Roll dropdowns are serialized according to the actual field type in that table, legacy JSON strings and Airtable choice objects are unwrapped, and Property State retains its audited Airtable storage alias while copied Listing State uses a clean postal code. Property dropdown values are validated against the audited live choices before the write, and legacy Fenced/Unfenced, Paved/Unpaved, Built to suit, and To be determined form values are mapped to the corresponding Yes, No, or Unknown Property choice. Intake saves write only fields still displayed on the Property form, so hidden legacy values such as Secondary Property Type are preserved in Airtable without being resubmitted. Airtable typecasting is disabled on intake writes, preventing the app from attempting to create select choices. Listing Basics separately presents Primary Use Type and an optional Secondary Property Type using the approved use choices; both listing-specific values are persisted as protected Listing metadata.
  • The Property Identity section presents Address, State, City, then Zip code so State is chosen before the dependent City input. City shows the prefilled Maine dropdown only when Property State is ME. For a blank or non-Maine state, City remains editable as a text input and does not display Maine city suggestions; changing state does not discard an existing City value.
  • Properties and individual Listings use one shared 36-field Industrial definition, covering industrial/warehouse square footage, heights, columns, cranes, loading doors, drive-in doors, insulation, foundation, lighting, rail, heating/cooling, electrical service, sprinklers, cleanroom, and wet-lab details. Clear Height accepts either a numeric-looking value or descriptive text. The Listing Industrial section is always available so blank or inconsistent Airtable use classifications cannot hide it, and it opens automatically when the Property or Listing is classified as Industrial, Flex/R&D, Data Center, or Life Science. Drive-In-Doors, Insulation, Foundation, and Lighting save to native free-text Airtable columns on both Properties and Listing Details. Other Listing-specific Industrial values remain in protected structured metadata in that Airtable Listing's Internal Notes; the marker is removed before Internal Notes are displayed or supplied as review context.
  • The listing edit page can permanently delete its Airtable Listing record after explicit browser confirmation; deleting a Listing does not delete its linked Property record.
  • The Properties page treats Airtable's current Listing records as authoritative, removes deleted Listing IDs from locally logged intake history, and presents one Open intake action that expands to a detailed chooser when multiple active Listings share a Property. Each Property also has a separate Edit property action that opens the Property intake step with that exact Airtable Property record preselected.
  • The Listings page groups loaded Airtable Listing records by their linked Property record ID and labels each group with the canonical Property address, keeping same-address text from merging distinct Property records. Each linked Property group includes Generate aggregate brochure, Create another listing, and Property-filtered Saved brochures actions; each Listing keeps its own Generate new brochure action. Legacy prior-PDF links are omitted from the index because saved versions are accessed through Saved brochures, while the individual Listing record retains the legacy PDF link for backward compatibility.
  • The primary navigation highlights exactly one current area: Listing intake for the new/edit-property intake form, Listings for the listing index and listing edit pages, Brochures for the saved brochure library/editor, and Properties for the property index.
  • The listing intake omits List Date, Expiration Date, Possession, Max Contiguous, Max Contiguous Unit, Divisible, Entire Floor, Renewal, Sublease and its expiration, Rent Escalation Type and Description, Lease Signage, Lease Show Instructions, and the standalone Listing-level Floor and Suite inputs. Existing Airtable values for omitted Listing fields are preserved. Floor and Unit / Suite remain available within each individual Spaces / Rent Roll row.
  • Sale and Sale/Lease Listing intake presents an expandable Lease Schedule beneath each Rent Roll tenant. Every current term or option is stored as its own linked Airtable Lease Schedule record with plain-text year/term/override labels, regular numeric annual-rent and increase fields, explicit display order, optional paired dates, and an optional footnote. Initial Listing submission creates the tenant and all nested terms in one operation; the Listing edit page supports adding, reordering, editing, and deleting those same rows without a second submission.
  • Lease Schedule validation requires a term label for every populated row, requires start and end dates to be supplied together, rejects an end before its start, and prevents simultaneous numeric and text-override values for rent or increases. Deleting a Rent Roll tenant deletes its linked schedule rows, and deleting a Listing cleans up its linked Lease Schedule, Rent Roll, and Space / Unit child records before removing the Listing.
  • Each Listing has its own Primary Use Type. Rent Negotiable, Former Tenant, and Internal Notes are not displayed as intake questions; existing Former Tenant and Internal Notes values are preserved, and protected Listing use/Industrial markers are stored without exposing that metadata in the form.
  • Expenses accepts either numeric-looking values such as 3.85 or descriptive text. All Expenses values are sent to Airtable as text to match the live text-compatible column, preventing decimal values from being rejected. Tenant is hidden only for Lease-only Listings and remains available for Sale or Sale/Lease.
  • The intake Media step links to one shared Property Pictures library while editing a Property and to a Listing-specific Pictures area while creating a Listing. Floor plans, site plans, and surveys are uploaded as ordinary Property pictures. Opening an existing Property or Listing picture panel migrates files from its retired -plans key into the ordinary picture key and removes the old copies. Property pictures upload once the Airtable Property exists and are available to every linked Listing; Listing pictures can be queued until Airtable returns the Listing ID.
  • Core Import Lab photo imports preserve scope: every image Core displays for the Property is offered, including Core's Street View and static-map snapshot image sources; Core Property photos save to property-{propertyId}, Core Listing photos save to listing-{listingId}, and deterministic Core media filenames prevent retry duplicates. Every attached Core flyer is inspected for individual high-resolution embedded raster photos while its facts are analyzed. Photo-like images are deduplicated by content, logos and low-information artwork are excluded, reviewer-selected flyer photos are re-extracted at import time, and those originals enter the same Property or Listing picture pool.
  • The Property add/edit form accepts numeric-looking or descriptive Land Size in acres and writes the value directly to Airtable's live text-compatible Land Size Acres field. It does not overwrite the separate Land Size SF field.
  • The Property add/edit form always shows the expanded Land section, including Land Size (Acres) and the related land/site inputs, regardless of the selected Primary Use.
  • The Property add/edit form does not expose or write the Airtable Wetlands field; any existing Airtable value is left unchanged.
  • The Land section includes numeric Frontage (Feet) and text Frontage Street inputs mapped to Airtable's Street Frontage (Feet) and Frontage Street Property fields.
  • Total Parking is submitted as text to match Airtable's live Property column type, while Covered Parking and Uncovered Parking retain numeric validation.
  • Airtable-formatted numeric Property values such as 180,000 or 12,500 SF are normalized to plain numeric input strings before the Property form renders them, preventing valid saved values from appearing blank in HTML number inputs.
  • The Property Utilities section contains exactly five free-text inputs: Electric, Fiber Optic, Sanitary Sewer, Water, and Gas. They write to the corresponding Airtable single-line text fields and accept availability, provider, capacity, or other descriptive details without dropdown coercion. Provider-type, provider-name, and Other Utility inputs are omitted and their existing Airtable values are preserved. Clear Height, Land Size Acres, and Grade Level Doors are also free-text Property inputs. Property and Listing intake use one separate free-text Power field for detailed industrial electrical specifications; the retired Amps, Volts, and Phase inputs are not displayed or written.
  • The always-available Building section exposes the existing Airtable Roof Type, HVAC, Elevator, and Miscellaneous fields. Elevator is freeform text matching its live Airtable single-line text column, so users can record details such as passenger, freight, or multiple elevators; the other fields remain freeform. Values load from and save back to the Property record and remain available as source-backed brochure facts.
  • Opening intake from a property-specific link loads and selects that exact Airtable Property record atomically, so the user does not need to select it again.
  • After Airtable confirms a new Listing submission, the intake redirects to the Listings page so the saved record is visible in the listing index.
  • Normalizes both forms into one display-safe NormalizedMarketingRequest; legacy marketing-request tables and pasted listing copy are not consulted by this production path.
  • Keeps internal notes available as source context while later public-copy filters prevent them from leaking into brochure copy.

Outputs

  • NormalizedMarketingRequest
  • Listing and Property source records
  • Initial facts, notes, links, and broker text

Fallbacks and guardrails

  • A Listing without a linked Property stops generation with an explicit error.
  • Missing optional form values remain blank; the generator never fills missing property facts from a legacy request source.

Where each field or item is pulled from

Listing record
Submitted Airtable Listing form record (Listing Details table).
Property record
The Airtable Property form record linked by the Listing's Property field.
Offering terms and status
Listing fields: Listing Type, Listing Status, availability, rent, price, lease, sale, tax, and income/expense fields.
Address and physical details
Property fields, except Specific Address and Specific City on the Listing override the Property address/city for that offering.
Industrial offering details
The individual Listing's protected Industrial values when present; otherwise the linked Property's shared Industrial values.
Tenant lease schedule
Lease Schedule records linked to the authoritative Listing, Property, and Rent Roll tenant, ordered by Display Order.
Property uses
Primary Use plus the selected Secondary Use value from the linked Property record; legacy Secondary Property Type data remains available during normalization.
Public and internal text
Listing Remarks (Public), Listing Remarks (Members Only), Internal Notes, and the Property Description stored in Airtable Notes. Property Description is public only for aggregate brochure copy; private Listing text remains context-only and is filtered from public copy.
Source files
app/intake/listing/ListingIntakeForm.tsxapp/intake/listing/[listingId]/edit/ListingEditForm.tsxsrc/components/LeaseScheduleEditor.tsxapp/intake/listings/ListingIndex.tsxsrc/components/CombinedBrochureGenerator.tsxsrc/services/listingIntake.tssrc/services/coreImport.tssrc/services/pdfPhotoExtraction.tssrc/domain/coreImport.tssrc/components/CoreImportLab.tsxsrc/services/listingIntakeFlyerRequests.tssrc/domain/listingIndustrialDetails.tssrc/domain/listingIntakeRules.tssrc/domain/leaseSchedule.tssrc/domain/marketing.ts
2

Generation component

Property, Listing, and media resolution

Assemble the complete source-backed request from the two Airtable forms plus shared Property media and Listing-specific pictures.

Inputs

  • Normalized Property + Listing request
  • Linked Rent Roll and Lease Schedule records
  • Property- and Listing-scoped image/PDF uploads
  • Links entered on the Listing form

What happens

  • Loads the Airtable source first so the exact linked Property ID is known, then loads Property uploads, Listing uploads, the saved draft, and broker catalog concurrently while recording per-stage server timing for performance diagnosis.
  • Converts all nonblank, public Property and Listing values into labeled source facts while excluding owner/internal/contact/link fields from the public facts table. Sale brochures also exclude Price Negotiable, Sale Terms, Financing Avail., Loan Assumable, Sale Signage, Agent Ownership, Sale Show Instructions, Concession/Concession Explained, and Personal Property/Personal Property Included fields without changing their Airtable values.
  • For an individual Industrial brochure, nonblank Industrial values saved on that Listing override the matching shared Property facts without duplicating rows or rewriting the Property. Aggregate brochures retain the shared Property Industrial facts because their selected Listings can describe different spaces.
  • Combines structured frontage feet and frontage street values into one public fact such as '670 FT on Camden St.' while retaining the separate Airtable fields.
  • Single-Listing brochures load that Listing's picture key before the shared Property library. Combined brochures load the shared Property picture library first, followed by any combined-brochure pictures and then each selected Listing's picture key in selection order. Legacy Property and Listing plan keys remain readable for backward compatibility, but their images are normalized into the same ordinary picture pool rather than labeled or laid out separately.
  • Pictures imported through Core Import Lab, including individual photos extracted from every attached Core flyer, enter those same Property- and Listing-specific upload keys only after the corresponding reviewed Airtable records exist, so later brochure generation continues to read Airtable plus its linked media rather than the Core feed.
  • Combined flyers load pictures from every selected Listing plus the shared Property picture library and legacy plan-key images, deduplicate upload asset IDs by source key, place common Property pictures before Listing-specific groups, and attach the authoritative Listing title as a small visible caption below every Listing-scoped picture in Page 2, Lease Information, gallery, and full-photo layouts. Each Property or Listing/unit group receives its own consecutive gallery and full-photo pages instead of sharing a gallery page with the next group. Shared Property pictures remain uncaptioned by default. The normal single-Listing generation path remains available.
  • Combined availability groups use Space Type first and Listing Primary Use Type as the fallback category. Five or fewer units render individually with source-backed square footage and asking rent or list price; larger sets render one minimum-to-maximum size and compatible price range.
  • Loads Lease Schedule records directly from the selected authoritative Listing records, sorts them by tenant and Display Order, and adds deterministic tenant-grouped schedule models to the normalized request and frozen preview source.
  • Preserves Listing-form links as supporting source links without treating pasted page copy as a generation input.
  • Applies saved draft location overrides before generation so the headline, facts, map, and PDF use the same address.
  • Versions each saved draft against the authoritative Property, Listing, and linked Lease Schedule source fields; changed source data invalidates stale generated text, facts, broker choices, market snapshots, and Lease Information page/photo choices before rebuilding the brochure.
  • Legacy editors continue storing edits under a Listing-specific key so drafts from retired intake sources cannot override rebuilt Property or Listing form values.
  • Generating from Listings or the combined Property selector creates a new saved brochure ID and freezes the resolved FlyerPreviewModel as that version's source snapshot; Airtable remains the authoritative input for creating later versions. A reviewer can supply the saved brochure name during generation; a blank name defaults to Property name — New York creation date.

Outputs

  • Resolved Property + Listing request
  • Tenant-grouped lease schedules
  • Ordered Property + Listing media
  • Form-backed fact set
  • Versioned source snapshot
  • Saved editor draft

Fallbacks and guardrails

  • A request can still generate without photos, using visible placeholders and a review note.
  • The linked Property address remains canonical unless the Listing form explicitly provides a specific offering address.

Where each field or item is pulled from

Normalized request fields
The linked Airtable Property and submitted Listing records, mapped in listingIntakeFlyerRequests.ts.
Property fact rows
Every nonblank public Property form field, excluding address/location coordinates, Owner, and Notes.
Listing fact rows
Every nonblank public Listing form field, excluding titles/status/address, remarks/internal notes, agents, Links / Videos, and the non-marketing sale administration fields Price Negotiable, Sale Terms, Financing Avail., Loan Assumable, Sale Signage, Agent Ownership, Sale Show Instructions, Concession details, and Personal Property details.
Lease Information rows
Linked Lease Schedule Airtable records grouped by their Rent Roll tenant and sorted by Display Order.
Listing pictures
Files uploaded under the Listing-specific picture key (listing-{listingId}).
Property pictures
Reusable files uploaded under the shared Property key (property-{propertyId}).
Legacy plan uploads
Images previously uploaded under Property or Listing plan keys remain readable and now enter the same ordinary picture pool as current Property and Listing pictures.
Saved edits
The brochure-specific local/server FlyerEditorDraftState for versioned brochures, with Listing-specific keys retained for legacy editors; these override generated display values only, not the Airtable source record.
Source files
src/services/listingIntakeFlyerRequests.tssrc/services/flyerPreviewRequests.tssrc/services/savedBrochures.tssrc/services/localUploads.tssrc/services/coreImport.tssrc/services/pdfPhotoExtraction.tssrc/domain/leaseSchedule.tssrc/services/flyerEditorDrafts.ts
3

Generation component

Template and page-plan selection

Choose the CoreFirms template family and establish the structured brochure model.

Inputs

  • Property type
  • Transaction type
  • Requested service
  • Core brand configuration

What happens

  • Industrial, land, retail, and office requests select their matching template families.
  • Other for-sale requests use the sale/investment family; remaining requests use the portrait brochure family.
  • Builds the headline, subheadline, page-two content, optional gallery pages, map page, broker block, and review notes as structured data.
  • Automatically adds Lease Information after Property Information only for Sale and Sale/Lease brochures with at least one valid linked schedule row. Ordinary For Lease availability brochures do not gain this investment page.
  • The first Lease Information page holds up to seven rows and up to three tenant groups followed by zero, one, or two selected photos. Additional tenant groups or rows continue on table-only Lease Information - Continued pages before the general property-photo pages, and later page numbers update automatically.
  • Uses a clean Campus Name for the cover when available; otherwise Retail, Industrial, and Office Property Type fallbacks are labeled Retail Space, Industrial Space, and Office Space, while other Property Type labels remain unchanged.
  • Uses the supplied CoreFirms reference-flyer directory as the visual source of truth for template changes.

Outputs

  • Template family
  • FlyerPreviewModel
  • Initial page sequence

Fallbacks and guardrails

  • Unknown combinations use portrait-brochure without preventing preview generation.

Where each field or item is pulled from

Template family
Normalized Property Type first; for unmatched types, normalized Listing Type distinguishes sale/investment from portrait brochure.
Brand colors and assets
Core brand configuration in src/domain/brand.ts and files under public/brand/core.
Page plan
The generated FlyerPreviewModel plus optional page-inclusion settings saved in the editor draft.
Source files
src/services/flyerGeneration.tssrc/domain/leaseSchedule.tssrc/domain/brand.tssrc/domain/marketing.tsAI_PROJECT_BRIEF.md
4

Generation component

Property facts and page-two table

Select concise, public-facing facts without inventing or calculating unsupported values.

Inputs

  • Property and Listing fields
  • Request facts
  • Description/notes
  • Property and transaction type

What happens

  • Normalizes labels, removes duplicate facts, and filters internal/admin/accounting/contact/link fields.
  • Prioritizes facts relevant to the transaction, such as availability, rent, sale price, size, acreage, parking, zoning, loading, utilities, and building details.
  • Labels the Listing form's sale asking price as List Price and automatically includes the nonblank value in Property Information for for-sale and sale-or-lease brochures. Aggregate sale brochures use the shared price or Varies when selected sale Listings differ.
  • For-sale Page 2 facts omit Price Negotiable, Sale Terms, Financing Avail., Loan Assumable, Sale Signage, Agent Ownership, Sale Show Instructions, Concession/Concession Explained, and Personal Property/Personal Property Included while preserving those source values in Airtable.
  • Property Information defaults to the fully resolved Address first, preserving common numbered street suffixes such as Trail and Trl, with nonblank Property Type directly below it and nonblank linked-Listing List Price next on for-sale or sale-or-lease brochures. One lease then renders as ordinary source-labeled space size, Lease Rate, Lease Type, and Expenses fact rows; two or more leases/units render in a dedicated source-backed Lease Rates block. Land Size, Lot Size, Acreage, and Site Size labels are classified as Lot Size before any generic building-size match, so acreage cannot be rounded and mislabeled as square feet. Lot Size, Building Size, Road Frontage, Year Built, roof, construction, HVAC, elevator, Zoning, and Parking Spaces follow in the default hierarchy; blank physical facts are omitted. Industrial brochures then append nonblank Clear Height, Dock-High Doors, Grade-Level Doors, Cross-Dock Doors, and Rail Doors from the effective Listing-over-Property Industrial values. Miscellaneous, when nonblank, is the final default row. Reviewers can move any visible single-row fact up or down or delete it from the brochure; explicit saved order and hidden-row keys win in the editor, preview, downloaded PDF, and map-editor round trip. Deleting a brochure row never clears its Airtable value, and the editor's Deleted inputs controls can restore any hidden row, including linked single-Listing rows. Choosing an available source field again from Add input also restores it. Each door row combines its count/value with its matching description when both are present.
  • Property Information uses one shared two-column grid for the Address, lease rows, and physical facts. Every right-column value is left-aligned to the same inset so values remain visually aligned regardless of text length. Address values use the same size and wrapping behavior as the other table values so they remain readable.
  • Combined flyers use the standard Property Information Page 2 header/footer and list each available unit's square footage and linked Listing rate from largest to smallest inside the Lease Rates block instead of mixing unit rows into the physical facts table.
  • Single-Listing lease summaries combine Listing Total Available with Total Available Unit, Asking Rent with Rent Unit, and Expenses with Expense Type before rendering those values as individual Property Information fact rows; Property Building Size is never substituted for missing unit availability.
  • Uses deterministic property-type and transaction rules to select from exact source-backed facts; initial rendering makes no fact-selection AI call.
  • The brochure preview and Page 2 editor use the same ordered physical-fact selector. It restores any nonblank automatic source row omitted by an older saved draft, including Property Type and Lot Size, keeps reviewer-added Property or custom rows visible in both places, and applies an explicit reviewer-saved row order after restoration.
  • Sanitary Sewer and Sanitary Sewer provider fields remain available in the authoritative Airtable Property record and Page 2 add-input menu, but are excluded from automatic Property Information fact selection. A reviewer-selected Sanitary Sewer row is marked as intentional, remains visible, can be reordered, and persists in preview and PDF; unmarked rows in older drafts are treated as legacy automatic selections and removed.
  • A single Listing's generated availability, Lease Rate, Lease Type, and expense rows are inserted in the same position in the editor for review and labeled as linked-Listing values.
  • Whole numbers remain whole, while every decimal number displayed in the Property Information table—including acreage, square footage, rates, and expenses—is rounded to exactly two decimal places. Square-footage sizes display as {value} +/- Sq. Ft. and acreage displays as {value} +/- Acres; this display-only formatting is shared by preview and PDF and does not duplicate source units or approximation markers.
  • The Page 2 add-input menu offers only writable Airtable Property fields whose current values are nonblank and are not already represented by an editor row or an automatically rendered Address, Lot Size, Building Size, Road Frontage, Year Built, roof, construction, HVAC, elevator, Zoning, Parking, or Industrial row; a custom input option remains available.
  • Retail lease flyers use the same Address-first, lease-count-aware detail layout, and ordered physical-facts hierarchy; missing optional rows remain omitted without adding a warning to Page 2.
  • Retail properties with Property Occupancy Type set to Single Tenant force available Address, Year Built, Square Footage, Acreage, and Parking values into the Page 2 fact table in that order.

Outputs

  • Formatted facts table
  • Source-backed facts for copy generation

Fallbacks and guardrails

  • Requests with few usable public fields show only the available exact facts; Page 2 does not display a missing-fields warning.

Where each field or item is pulled from

Address row
Listing Specific Address/Specific City when present; otherwise Property Address/City, plus Property State and Zip code.
Property type row
Property Type, falling back to Primary Use and then Secondary Property Type.
Deal type row
Listing Type, normalized to For Lease, For Sale, or Sale or Lease.
Location row
Resolved city and Property State.
Single-Listing lease size and rate
Listing Total Available + Total Available Unit and Asking Rent + Rent Unit; these paired values take precedence over generated or saved fact rows and render as individual rows using the Airtable Total Available label.
Single-Listing estimated expenses
Listing Expenses plus Expense Type, combined without inventing a PSF unit or tax-inclusion statement.
All other fact rows
Exact nonblank public Property/Listing field values selected by deterministic property-type and transaction rules.
Editor-added or edited rows
Accepted fact-row edits in the Listing-specific FlyerEditorDraftState, visibly replacing generated rows in the reviewed model.
Source files
src/services/flyerGeneration.tssrc/services/listingIntakeFlyerRequests.tssrc/domain/flyerLeaseRules.tssrc/domain/pageTwoPropertyFields.tssrc/components/FlyerPreview.tsxsrc/components/FlyerTextPreviewEditor.tsx
5

Generation component

Description

Assemble the public-facing brochure description from authoritative source text and exact facts.

Inputs

  • Source description
  • Selected facts
  • Property and transaction type
  • Optional editor instruction

What happens

  • Cleans source text to remove URLs, photo-folder instructions, internal requests, contact details, and non-description lines.
  • Listing Remarks (Public) is the authoritative initial Page 2 description and is used directly after safety cleaning.
  • For an aggregate brochure, the shared Property Description takes precedence over the selected Listings' Public Remarks; combined Public Remarks remain the fallback when the Property Description is blank.
  • Saved Page 2 copy is reused only while it matches the current Public Remarks source version, so updated remarks replace stale generated drafts without discarding later intentional edits.
  • When a single Listing's Public Remarks is blank, DeepSeek automatically writes exactly two concise paragraphs using only the resolved address, property/transaction context, and selected public Airtable-backed facts.
  • Page 2 never renders a separate Building Highlights subsection; exact property details remain in the Property Information facts.
  • The request editor and saved-brochure editor can ask DeepSeek to improve description tone, emphasis, or length and must accept the suggestion before it replaces the draft. Saved brochures use their own matching improve-description API endpoint instead of falling through to a missing route. Unless the reviewer asks for a different length, an improved description returns as exactly two short paragraphs.
  • AI responses are normalized so soft line wrapping is joined inside each paragraph, common markdown prefixes are removed, and explicit blank-line paragraphs are preserved. A legacy long description saved as one block is balanced into two paragraphs at the nearest safe sentence boundary, while abbreviations such as U.S. are excluded from boundary detection.

Outputs

  • Source description or two-paragraph generated overview
  • Optional reviewed DeepSeek suggestion

Fallbacks and guardrails

  • If automatic DeepSeek generation fails for a blank single Listing, a deterministic two-paragraph description is assembled from transaction, property type, location, availability, and selected facts.

Where each field or item is pulled from

Source description
Single Listing: Listing Remarks (Public). Aggregate brochure: Property Description stored in Airtable Notes, falling back to the selected Listings' Public Remarks.
Overview
The cleaned authoritative source description directly; for a blank single Listing, DeepSeek is constrained to the address and selected public Airtable-backed facts.
Reviewer changes
Direct editor text input or an explicitly accepted DeepSeek suggestion stored in FlyerEditorDraftState.
Source files
src/services/flyerGeneration.tssrc/services/deepseekDescription.tssrc/components/FlyerTextPreviewEditor.tsxapp/api/brochures/[brochureId]/improve-description/route.ts
6

Generation component

Photo qualification, cover choice, and sequencing

Choose a strong cover image and a useful, non-repetitive gallery sequence.

Inputs

  • Property- and Listing-scoped uploaded images
  • Brochure-only pictures uploaded in the preview
  • Filenames and labels
  • File size and source metadata
  • Property context

What happens

  • Rejects non-image assets and penalizes floor plans, maps, logos, headshots, documents, thumbnails, and low-context images for cover use.
  • There is no dedicated Plans upload or plan-only brochure treatment. Images stored under legacy plan keys remain available and are labeled as ordinary uploaded pictures.
  • Uses deterministic metadata scoring only; photo selection does not call an AI or inspect image pixels during brochure generation.
  • Prefers filenames and labels suggesting exterior, aerial, campus, storefront, street-facing, and other context-rich images.
  • Supporting images are ordered from broad exterior/context through entrances, interiors, amenities, parking/loading, and detail shots. In combined brochures, shared Property pictures form the first group and the deterministic sequence is then applied within each selected Listing group.
  • An included Lease Information page defaults to the first two non-cover supporting Listing pictures. Its two editor selectors can replace either choice or leave it blank; chosen Lease Information pictures are removed from the general gallery so the same image is not repeated, and only the first Lease Information page displays the selected zero, one, or two-photo layout.
  • The preview's clearly labeled Upload replacement cover control and Pictures section save new images into the same brochure-specific persistent storage. A cover upload is assigned a durable brochure asset ID before the preview changes, then that ID and the uploaded asset are stored in the editor draft so the browser preview, saved brochure, and downloaded PDF use the same cover. The cover control confirms when the replacement is saved for PDF downloads. The Pictures uploader waits for the saved draft to load, reconciles any already-stored uploads into the selectable list, and lets newly uploaded pictures join the current gallery immediately, be promoted to the cover or Page 2, reordered, removed, and reopened later.
  • Keeps every valid supporting image and creates as many four-photo gallery pages as needed after the cover and Page 2 selections. In combined brochures, page construction finishes one Property or Listing/unit photo group before starting the next, including full-page choices, so unit photos remain together.
  • The editor can replace the cover, choose the page-two image, reorder the gallery within the preserved source groups, edit or clear each visible photo caption, or assign any supporting picture to its own full photo page. The Pictures sidebar displays Page 2 first and then flattens the exact grouped gallery/full-page plan used by the brochure, so cards remain in increasing brochure page/position order even when an older saved drag order interleaves Property and Listing groups. Caption overrides persist by asset ID, and photo layouts reserve a separate one- or two-line caption row so portrait images cannot cover the caption. The choices persist in the saved brochure draft and PDF.

Outputs

  • Cover asset ID
  • Supporting-photo IDs in order
  • Selection reasons and quality notes

Fallbacks and guardrails

  • No-image requests generate with placeholders and a visible review warning.

Where each field or item is pulled from

Candidate images
Property- and Listing-scoped uploaded files, resolved Airtable/Drive image assets attached to the normalized request, and brochure-only uploads saved with the editor draft.
Cover and gallery order
Deterministic image scores derived from filename, label, source, file size, original asset order, and property type.
Image metadata
Uploaded asset filename, label, source, and file size.
Manual image choices
Cover, page-two, Lease Information, grouped gallery-order, caption, and full-page picture overrides stored in FlyerEditorDraftState.
Source files
src/services/coverImageSelection.tssrc/components/FlyerTextPreviewEditor.tsxsrc/components/RequestUploadPanel.tsxsrc/domain/leaseSchedule.tssrc/services/localUploads.tssrc/domain/flyerEditorDraft.tssrc/services/flyerEditorDrafts.ts
7

Generation component

Location and amenities map

Build property-type-specific area context and a branded static map page.

Inputs

  • Resolved address/coordinates
  • Property type
  • Amenity rules
  • Mapbox, OSM/Overpass, Nominatim, Logo.dev

What happens

  • Returns the brochure preview immediately with a pending map model instead of blocking the initial server render.
  • After the editor loads, a client request starts map research and shows a live loading indicator until it completes or fails.
  • Geocodes the property and selects category/radius rules for office, retail, industrial, multifamily, land, or mixed use; Mapbox and Nominatim attempts are isolated so a Mapbox error still falls through to OpenStreetMap geocoding and later normalized address candidates.
  • Searches multiple place providers, widens initial search radii where needed, supplements partial or throttled Overpass responses with deduplicated viewport-wide Nominatim searches, deduplicates candidates, and scores distance, category, prominence, and recognizable brands.
  • For retail and mixed-use properties, recognized on-site brands explicitly described as tenants or anchors in authoritative Airtable Listing remarks are retained as property callouts with verified brand logos, even when external place providers time out or rank them out.
  • A Listing-specific address is used for map geocoding only when it contains a usable street address; floor- or suite-only values fall back to the linked Property address.
  • When the property-type search returns no logo-bearing places, automatically runs the broader nearby-business search across a wide map viewport and prioritizes its verified logos before rendering the map.
  • Previously saved maps that contain imagery or coordinates but no logo-bearing places are researched again automatically in both the flyer editor and map settings. Map settings also retry underfilled saved searches until they contain at least 16 resolved place logos or reach the reviewer's lower callout limit, while manual hiding, positioning, sizing, and custom logos remain authoritative.
  • Refreshing nearby businesses from map settings derives the search radius from the current static-map viewport. Zooming out expands provider search bounds beyond the former 12-mile ceiling, up to a 50-mile safety cap; zooming in contracts the radius to the visible area with a 1.25-mile minimum. The returned status names the actual radius searched.
  • Saved brochure map research and nearby-logo refreshes use the brochure version's frozen request data instead of re-resolving its current Airtable Listing-to-Property relationship. If the property-type research returns no logos, the saved viewport and callout limit drive a broader business-logo recovery search. An empty saved place list is treated as incomplete state and cannot erase the frozen base map's places; the map editor also carries the displayed base places into the next draft and repopulates orphaned manual-position drafts when no nonempty override exists. Every map-editor save retains the map address label so later server loads can validate that its viewport, places, and icon positions still belong to the same property. A valid custom-logo-only map remains authoritative.
  • Deduplicates rendered map logos by normalized brand label and logo URL, retaining custom or manually positioned versions when duplicates compete.
  • Automatically spreads unlocked map logos across available map space by balancing proximity to each place with logo clearance, outward distribution, short leader lines, and strong penalties for line crossings or lines passing through other logos; manually positioned logos remain fixed.
  • AI may rank the strongest commercial callouts; labels and places are limited to supplied candidates.
  • Persists completed map coordinates, places, and imagery in the Listing-specific editor draft so later visits and PDF exports reuse the researched map.
  • The map editor controls viewport, callout count, logos, leader lines, manual logo positions/sizes, custom logos, and whether the map page is included. Arrow controls pan the actual saved map center without forcing the SITE pin back to the middle, so the reviewer can shift the property toward a safe edge and make room for logos concentrated on one side; preview and PDF use the same projected pin and place positions. Viewport zoom uses the explicit controls so mouse-wheel page scrolling does not alter the map, and toggling the map page preserves the brochure editor's viewport position so the sidebar stays visible in stacked responsive layouts. Clicking a rendered logo outlines it and shows its source-backed place or custom-logo name in an editor-only selection badge with a Remove logo action. Removing a researched place logo adds its name to the saved hidden-logo list; removing a custom logo deletes that custom-logo override. The selection badge and action are excluded from the saved brochure view and PDF exports. Hidden-logo choices are saved before brochure PDF rendering, PDF generation stops if its required pre-save fails, and navigation or browser-back unloads flush the latest map draft immediately so removed logos cannot return in exports.
  • Unsaved maps start 0.4 zoom levels closer than the source static-map default; saved reviewer viewport settings remain authoritative.

Outputs

  • MapResearchResult
  • Static map image URL
  • Ranked places and logos
  • MapDisplaySettings overrides

Fallbacks and guardrails

  • The pending map remains needs-review and the editor shows an error if background geocoding or provider research fails.
  • If the automatic business-logo search times out or finds no verified logo, the map keeps any useful access callouts from the property-type search.
  • Manual map context and viewport controls remain available.

Where each field or item is pulled from

Property location
Property Latitude/Longitude when usable; otherwise the resolved Listing/Property address is geocoded.
Nearby places
Mapbox and OpenStreetMap/Overpass candidates, geocoded with Nominatim where required.
Named on-site tenants
Recognized brands explicitly named as tenants or anchors in authoritative Airtable Listing public remarks.
Place ranking and summary
Provider candidates scored by mapResearch.ts; AI may rank only those supplied candidates.
Logos
Logo.dev for recognized place brands, plus reviewer-uploaded custom logos.
Map appearance
Automatic property-type rules, then viewport/callout/logo overrides in FlyerEditorDraftState.
Source files
src/services/flyerGeneration.tssrc/services/mapResearch.tssrc/domain/mapTenantAnchors.tssrc/components/FlyerTextPreviewEditor.tsxsrc/components/FlyerMapSettingsEditor.tsxapp/api/airtable-requests/[requestId]/map-research/route.tsapp/api/brochures/[brochureId]/map-research/route.tsapp/api/brochures/[brochureId]/map-places/route.tssrc/domain/marketing.ts
8

Generation component

Market snapshot and demographics

Add an optional, concise Census-backed demographic overview without fabricating statistics.

Inputs

  • Property municipality
  • Property state
  • Property ZIP
  • Census ACS data

What happens

  • Builds up to six Census-backed metrics when a valid ZIP/state and Census response are available.
  • Uses the fixed Market Demographics title and constructs the subtitle directly from the authoritative Property location; this stage makes no AI call.
  • Positive trend graphs are built from supported Census time-series metrics, sanitized, and limited to three rows.
  • Renders six numbered metric cards in a three-column grid and groups supported trends in a high-contrast growth-indicators panel with the Census source visible in the page hero.
  • Metric cards omit their secondary detail captions to keep the page scannable.
  • The editor automatically generates and includes the first snapshot immediately after saved-draft loading, without waiting for map research, saves it with the draft to avoid repeated generation, and still supports explicit regeneration or exclusion.
  • Saved brochures generate demographics through their own brochure API route, which falls back to the brochure's authoritative source location when editor context is incomplete.
  • Legacy drafts without an explicit market-snapshot preference marker adopt the current included-by-default behavior; newly saved reviewer exclusions remain authoritative.

Outputs

  • Fixed page title
  • Location-based subtitle
  • MarketSnapshot metrics
  • Optional trend graph
  • Source/warning label

Fallbacks and guardrails

  • When Census data is unavailable, the deterministic snapshot uses available Property and map context and reports the missing Census context.

Where each field or item is pulled from

Demographic metrics
U.S. Census ACS data resolved from the Property ZIP/state/location.
Market title
Fixed Market Demographics template label.
Market subtitle
Property municipality/state, then ZIP fallback.
Trend graph
Supported Census time-series metrics only.
Include/exclude choice
Included by default in the generated model; an explicit reviewer preference in FlyerEditorDraftState overrides that default.
Source files
src/services/marketDemographics.tssrc/components/FlyerTextPreviewEditor.tsxapp/api/airtable-requests/[requestId]/market-snapshot/route.tsapp/api/listing-requests/[requestId]/market-snapshot/route.tsapp/api/brochures/[brochureId]/market-snapshot/route.ts
9

Generation component

Broker matching and contact block

Resolve approved CoreFirms broker profiles and keep contact data consistent across pages.

Inputs

  • Broker text and linked team fields
  • Broker profile index
  • Editor broker selection

What happens

  • Matches request broker text to managed broker profiles.
  • For Listing-form submissions, resolves Primary Agent, Secondary Agent, and Tertiary Agent in that submitted order before falling back to broad broker text matching.
  • Empty or stale saved broker selections fall back to the brokers resolved from the current Listing submission, and restored selections preserve their submitted order.
  • Adds approved names, titles, phone numbers, emails, headshots, and display order to the model.
  • Renders each matched broker headshot in generated flyer contact footers, with cover, compact, map, and feature layouts controlling its display size.
  • The editor can select the profiles used in the final contact block; saved slugs persist with the draft.

Outputs

  • Ordered BrokerProfile list
  • Contact block and headshots

Fallbacks and guardrails

  • Unmatched broker text does not create invented contact information.

Where each field or item is pulled from

Broker selection and order
Listing Primary Agent, Secondary Agent, and Tertiary Agent in that order; reviewer selection can override the displayed set.
Name, title, phone, email, and headshot
Matched managed broker JSON profile in data/brokers and its referenced public/brokers asset.
Saved broker override
Broker profile slugs stored in the Listing-specific FlyerEditorDraftState.
Source files
src/services/brokerProfiles.tssrc/domain/brokerProfiles.tsdata/brokers/index.json
10

Generation component

Editable preview and draft persistence

Let a reviewer change structured flyer content without patching the PDF directly.

Inputs

  • FlyerPreviewModel
  • Text edits
  • Fact rows
  • Cover and supporting photo choices
  • Lease Information page/photo choices
  • Photo order and reversible brochure-only photo removals
  • Broker selection
  • Map settings
  • Optional AI suggestions

What happens

  • Applies text, fact, photo, broker, map, market-snapshot, and page-inclusion overrides to the structured model.
  • Provides an Additional pictures uploader inside the preview photo controls after saved-draft restoration completes. Both newly completed uploads and files already present in brochure upload storage hydrate the selectable Pictures list; genuinely new uploads are placed first for immediate Cover, Page 2, or gallery assignment. These brochure-only uploads persist as additionalPhotoAssets in the draft, append to the frozen source assets when reopened, and are included in browser previews and server-rendered PDFs.
  • Provides Remove on every supporting picture and a Removed from this brochure area with individual and bulk restore controls. Removing a picture clears any Page 2, Lease Information, or full-page assignment, records its asset ID in removedPhotoAssetIds, and excludes it from preview and PDF while retaining the original source/upload asset for restoration.
  • Keeps each picture card tall enough for both rows of Cover, Page 2, Full page, Up, Down, and Remove controls inside the scrollable editor list, so the controls remain fully visible instead of being clipped by the next picture.
  • Orders the Pictures sidebar from Page 2 through the grouped gallery/full-page plan used by the rendered brochure, rather than displaying an interleaved legacy supportingPhotoAssetIds sequence whose placement labels jump backward and forward between pages.
  • Provides an editable Caption field on every included picture. Overrides, including an intentionally blank caption, save by asset ID and apply consistently to Page 2, Lease Information, gallery, full-page, browser-preview, and downloaded-PDF layouts.
  • Lets a reviewer interchange the cover with any Page 2 or gallery photo, always returns the outgoing cover to the promoted photo's supporting position, repairs saved orders that omitted the former cover, and saves the selected cover asset for browser previews and server-rendered PDFs.
  • Provides a Lease Information editor section when source-backed schedule rows exist. The reviewer can hide the page or select its two photos, but schedule facts remain editable only through the authoritative Listing intake; the inclusion and photo choices are saved in the draft and accepted only while its Lease Schedule source signature still matches.
  • Saves drafts locally and to the server with request-scoped storage; server storage can use Postgres or the configured fallback. The editor keeps auto-save enabled, shows its live status in a persistent bar at the top of the controls, and provides Save changes for immediate confirmation.
  • Navigation between a saved brochure and its map editor performs an awaited full-draft save before changing pages. Client draft writes are serialized so an older in-flight auto-save completes before the navigation save. The map editor seeds fallback drafts with the active cover, supporting-photo order, removed-photo choices, Page 2 photo, full-page-photo choices, brochure-only uploads, and source signatures so map auto-save cannot overwrite newer brochure presentation work.
  • Stores an authoritative source-data signature with each draft. When Property or Listing details change, source-derived text, facts, broker selection, and market snapshot are regenerated from the new forms instead of being overwritten by the stale draft.
  • Source-independent presentation choices such as map settings, selected cover, photo order, selected page-two photo, and ordinary page-inclusion toggles remain available across source updates when the referenced assets still exist. Lease Information inclusion and photo choices are intentionally source-versioned because the linked rows determine whether and how that page exists.
  • Validates and sanitizes saved state before applying it to a newly regenerated base model.
  • Saving an edited Airtable-backed Page 2 value writes the nonblank value through to the linked Property record; Airtable-backed labels remain fixed while custom labels and values remain editable. Current-source editors refresh their source signature, while a saved brochure retains its frozen version signature so the same edit remains visible in that version after Airtable changes.
  • Every visible single-row Property Information input has a Delete action. Deletion stores its label in hiddenPageTwoFactKeys and compares normalized keys so automatic Property rows and linked single-Listing lease rows stay hidden in that brochure preview and PDF without clearing Airtable. Deleted inputs controls restore any hidden row, while selecting an available source field again also removes its hidden key. Blank edited values are not written back.
  • Accepts the optional saved map address label and description source signature so drafts written by newer editor clients remain backward-compatible during deployment transitions.
  • Reconciles server-saved and browser-local fact rows through the same selector used by the preview, so older drafts restore available automatic Property rows such as Lot Size unless that draft explicitly contains the row's hidden key. Reviewer-edited values retain precedence, and reviewer-added rows remain visible in the rendered table.
  • Displays a single Listing's source-backed availability, Lease Rate, Lease Type, and expense rows in the same Page 2 editor order as the preview, marked as linked-Listing values. Linked-Listing rows use the same Edit and Done controls as other Property Information rows. Their source labels remain fixed; value typing stays local until the reviewer clicks or tabs away, then a nonblank value is saved as a brochure-version override and used by the preview and downloaded PDF without changing the authoritative Airtable Listing. Blank edits revert with guidance to use Delete when the row should be hidden.
  • Shows a shared, reduced-motion-accessible CORE loading screen while either Airtable-backed or Listing-backed brochure preview data is assembled.
  • Places the sticky editing controls to the left of the live brochure preview on desktop and stacks the preview above the controls on narrow screens.
  • Generate new brochure always creates a distinct brochure ID, property-level version number, frozen source model, independent autosaved draft, and reviewer-entered name; when the name is blank, it uses Property name — New York creation date. It never replaces an existing version.
  • Saved Brochures explains that generation saves the version immediately, lists prior versions by Property, reopens their frozen source model for continued editing, and supports later renaming. Create new version clones the current frozen model and saved edits into a separate version. Regenerate brochure instead refreshes the current saved brochure in place from the latest Airtable Property and Listing data, preserving its brochure ID, name, version number, and compatible presentation overrides while updating its frozen source model and source signature. When Airtable has changed, the source alert directs the reviewer to the same always-visible Regenerate brochure action. Permanent deletion removes only that version and its saved draft; Airtable source records and other versions remain unchanged.
  • Page 2 edits inside a saved brochure always update that brochure's draft. Recognized Airtable Property fields also write through to the linked Property so future brochures and Property Details use the corrected value, while custom rows remain brochure-only. Download PDF is labeled separately from brochure saving so exporting cannot be mistaken for persistence.
  • Reset removes saved overrides and returns to source-generated content.

Outputs

  • SavedBrochureRecord
  • FlyerEditorDraftState
  • Updated preview model
  • Persisted review state

Fallbacks and guardrails

  • Drafts created before source signatures were introduced are treated as stale once, so current Airtable values win instead of silently preserving unknown generated content.
  • If a stored override no longer matches the current map/assets, incompatible pieces are discarded safely.

Where each field or item is pulled from

Base preview
Fresh generation from the current authoritative Listing and linked Property records when creating a version; reopening uses that version's saved FlyerPreviewModel snapshot.
Saved brochure name
Reviewer input at generation or later rename; blank generation input falls back to the Property title plus the America/New_York creation date.
Text, fact, photo, broker, map, snapshot, and page overrides
Reviewer actions in FlyerTextPreviewEditor, including Lease Information inclusion and its two photo asset IDs, saved as FlyerEditorDraftState under the brochure ID for versioned brochures or the Listing-specific key for legacy editors.
Reset values
A newly generated base model; reset deletes the saved display overrides.
Source files
src/components/FlyerTextPreviewEditor.tsxsrc/components/FlyerMapSettingsEditor.tsxsrc/components/FlyerPreview.tsxsrc/domain/pageTwoPropertyFields.tssrc/components/RequestUploadPanel.tsxsrc/components/GenerateBrochureButton.tsxsrc/components/RegenerateSavedBrochureButton.tsxsrc/components/SavedBrochureNameEditor.tsxsrc/components/SavedBrochureActions.tsxapp/brochures/[brochureId]/page.tsxapp/api/brochures/[brochureId]/route.tsapp/api/brochures/[brochureId]/regenerate/route.tsapp/api/brochures/[brochureId]/editor-draft/route.tsapp/api/airtable-requests/[requestId]/property-field/route.tssrc/services/savedBrochures.tssrc/domain/savedBrochureRules.tssrc/services/listingIntake.tssrc/components/CoreLoadingScreen.tsxsrc/services/flyerEditorDrafts.tssrc/domain/flyerEditorDraft.tsapp/globals.css
11

Generation component

Page composition and layout fitting

Render the structured model as CoreFirms-branded landscape brochure pages without overflow.

Inputs

  • Edited preview model
  • Template family
  • Photos
  • Map
  • Market snapshot
  • Broker profiles

What happens

  • Composes cover, information/description, optional tenant-grouped Lease Information, photo gallery, market snapshot, map, and broker-footer sections.
  • Renders Lease Information with YEAR, START, END, TERM, RENT, and % INC columns. Dates use MM/DD/YYYY, numeric rent uses currency, numeric increases retain zero and use one decimal place, and entered TBD/other overrides and footnotes are preserved. Tenant headings stay with their rows when possible, overflow pages are table-only, and a missing valid schedule produces no blank page.
  • Uses fixed print-page dimensions, brand colors/assets, and template-specific classes.
  • Styles the cover title in a condensed medium-bold sans serif with the reference dark navy color so campus names and Property Type fallbacks share the same tall, clean treatment.
  • Measures the cover-title font actually available in the browser or server PDF renderer. When the PDF server lacks the preview's condensed font, the title retains its original height and is condensed horizontally only as much as needed to preserve the reviewed one-line layout; unusually long titles retain a readable wrapped fallback.
  • The dedicated map editor scales its full map page to the available preview column while keeping the settings panel alongside it. The main brochure editor stacks its controls below the fixed-size preview when a viewport is too narrow for both columns, so the complete right side of every page—including a loaded map—remains visible. Print and PDF dimensions remain unchanged.
  • Property photo gallery tiles use contained image fitting so the full uploaded image remains visible in both preview and PDF output; this gallery-specific rule does not change cover or Page 2 image cropping.
  • A fit controller measures content and applies constrained scale/spacing adjustments before declaring the page print-ready.
  • The same FlyerPreview component is used for the web preview and print route so PDF output matches the reviewed model.

Outputs

  • Responsive web preview
  • Print-ready DOM pages
  • PDF readiness markers

Fallbacks and guardrails

  • Review notes and placeholders remain visible when required content or images are unavailable.

Where each field or item is pulled from

Page content
The edited FlyerPreviewModel assembled from generated values and accepted reviewer overrides, including source-backed Lease Schedule groups and source-versioned Lease Information presentation choices.
Page dimensions and styling
FlyerPreview template markup, Core brand configuration, and print rules in app/globals.css.
Fit adjustments
Runtime DOM measurements made by FlyerLayoutFitController.
Source files
src/components/FlyerPreview.tsxsrc/components/FlyerLayoutFitController.tsxapp/globals.css
12

Generation component

PDF generation, links, and delivery

Turn the reviewed web model into the downloadable brochure and deliver review links.

Inputs

  • Request/flyer ID
  • Optional inline editor draft
  • Print route
  • Ready-state selectors

What happens

  • The production PDF endpoint opens the matching saved-brochure, Airtable Listing, or legacy Listing print route in Playwright/Chromium.
  • For a saved brochure POST, the endpoint persists the exact submitted editor draft before navigation and lets the print route read that stored draft instead of placing the full photo, map-place, and text payload in the print URL. Legacy request routes retain their inline draft compatibility.
  • It waits for page load, fonts, images, layout fitting, and print-ready markers before rendering.
  • PDF pages use 11 × 8.5 inch landscape sizing, CSS page sizes, zero margins, and printed backgrounds.
  • Serverless Chromium launches with unnecessary WebGL graphics disabled. If the browser or page still closes unexpectedly, the endpoint closes the failed process and retries once with a fresh browser.
  • Brochure-editor and map-editor downloads save the current structured draft, then capture the already-rendered preview pages sequentially in the user's browser and assemble them into an 11 × 8.5 inch PDF. Each capture uses that page's computed background color so dark cover or closing pages retain their white text and branding. Image embedding caches by the complete asset URL, including query parameters, so distinct uploaded files served through the same upload route cannot reuse the first picture. This production path avoids launching a separate serverless Chromium process, while preserving the exact editor preview and bounding capture work to one page at a time. Direct server rendering remains available for non-editor workflows; it bypasses Chromium's unstable Page.printToPDF protocol on Vercel and retains native print-to-PDF plus the page-image fallback outside serverless production.
  • The Airtable brochure preview uses the current Download PDF workflow and does not show a redundant toolbar link to a previously generated brochure.
  • The automation writes review/PDF links to Airtable and can send the requester a delivery email through the configured provider.

Outputs

  • Downloadable PDF
  • Review URL
  • Airtable PDF/Magic Link fields
  • Delivery status

Fallbacks and guardrails

  • An unexpected target-page, browser-context, or browser-process closure receives one clean-browser retry before the error is returned.
  • Interactive editor downloads build the PDF from the live preview in the user's browser, one page at a time, so a serverless Chromium exit cannot interrupt the download. Direct server rendering streams images page by page into the image PDF on Vercel; in non-serverless rendering, a Page.printToPDF or Printing failed protocol error uses that same PDF assembler and unrelated render errors remain visible instead of being masked.
  • Rendering errors return an explicit API error and automation status instead of a corrupt PDF.
  • Email delivery supports log-only mode when a production provider is not configured.

Where each field or item is pulled from

Rendered brochure
The saved brochure print route uses its frozen source model plus its brochure-specific inline/saved draft; legacy routes continue using their Listing model and draft.
PDF file
A page-by-page image PDF after fonts, images, fit controls, and ready markers complete in serverless production; non-serverless rendering uses Playwright print-to-PDF with the same image PDF as its guarded fallback.
Review and PDF links
Application URLs built for the Listing/flyer ID and written to the Airtable Listing delivery fields.
Delivery email
Configured email provider, recipient/delivery context, and generated review/PDF links.
Source files
app/api/flyers/[flyerId]/download.pdf/route.tsapp/admin/airtable-requests/[requestId]/page.tsxapp/brochures/[brochureId]/print/page.tsxapp/admin/listing-requests/[requestId]/print/page.tsxsrc/services/email.tssrc/services/siteMarketingRequests.ts

Generated from source

Live instruction catalog

These are extracted directly from project guardrails, AI system messages, prompts, and instruction properties. They update automatically during build.

Flyer-generation wikiAGENTS.md:19

- The Airtable Property form plus its linked Listing form are the only authoritative production inputs for flyer generation. Do not add a legacy marketing-request table, pasted listing copy, or another intake source to the production generation path unless the user explicitly changes this rule. - Treat `/wiki/flyer-generation` as required product documentation for the flyer pipeline. - When changing flyer-generation behavior, prompts, guardrails, fallbacks, data sources, layout rules, editing behavior, map/photo selection, rendering, PDF generation, or delivery, update the matching stage in `src/domain/flyerGenerationWiki.ts` in the same change. - Do not hand-edit `src/generated/flyerGenerationWikiSnapshot.json`. Run `npm run refresh-wiki`; production builds run it automatically. - Add any new prompt-bearing flyer source file to `instructionSourcePaths` in `scripts/update-flyer-generation-wiki.mjs` so its instructions appear automatically in the live instruction catalog. - Keep source-file references in the wiki accurate and prefer descriptions of current behavior over future plans.

North StarAI_PROJECT_BRIEF.md:4

Build a CoreFirms-branded flyer automation platform that turns Airtable marketing requests into editable web previews and PDFs.

Non-NegotiablesAI_PROJECT_BRIEF.md:14

- Match CoreFirms flyer style before inventing new visual systems. - Use an editable structured flyer model as the source of truth. - Regenerate PDFs from structured data; do not blindly patch raw PDFs. - Airtable is intake-only; the app owns downstream state. - Broker access uses scoped magic links. - AI can make visual edits, but only through validated document operations.

Core WorkflowAI_PROJECT_BRIEF.md:23

1. Airtable request received. 2. Google Drive photos imported. 3. Template or AI layout selected. 4. Amenities map researched and rendered. 5. Web preview and PDF generated. 6. Broker email sent automatically. 7. Broker edits through AI chat. 8. New flyer version rendered and archived.

Architecture DefaultsAI_PROJECT_BRIEF.md:34

- Next.js + TypeScript. - Managed Postgres. - Redis-backed job queue. - S3/R2-compatible object storage. - Playwright/Chromium PDF rendering. - OpenAI structured outputs for AI layout/edit operations. - Mapbox static maps for final map images. - Postmark or equivalent company-domain transactional email.

AI GuardrailsAI_PROJECT_BRIEF.md:70

- AI must output structured JSON operations. - Validate schema, required fields, brand colors, page bounds, overflow, image references, and broker info before saving. - Preserve version history. - Low-confidence map research may still auto-send, but must include a visible review note.

instructionsrc/services/flyerGeneration.ts:144

Write exactly two concise paragraphs for the brochure. Use only the supplied address, property context, and Airtable-backed Page 2 facts. Do not mention missing information. Do not add a Building Highlights heading, subsection, or bullet list. Return description paragraphs only.

instructionssrc/services/mapResearch.ts:1225

Select the strongest commercial real estate brochure map callouts. Prefer the most popular, largest, or most recognizable places over small generic POIs when they are comparably close: national/regional brands, major shopping centers, large grocers, hospitals, colleges, hotels, employers, transportation infrastructure, and high-signal places with website/wikidata/source detail. Choose context-dependent places: retail should emphasize demand generators, co-tenants, parking, and access; industrial should emphasize highways, rail, ports, airports, labor access, and logistics context. Use only candidate IDs and return concise labels.

system messagesrc/services/deepseekDescription.ts:75

You are a commercial real estate brochure copy editor for CoreFirms. Rewrite property description copy so it sounds polished, specific, and professional. Follow the optional editingInstruction for length, tone, emphasis, or style when it is compatible with the source facts. Preserve all factual claims. Do not invent square footage, rates, pricing, zoning, tenants, parking, dates, amenities, or broker details. Do not add headings, bullets, markdown, URLs, phone numbers, or emails. Return only the improved property description. Default to exactly 2 short paragraphs unless the editingInstruction explicitly asks for longer or shorter copy. Separate paragraphs with exactly one blank line, and do not insert line breaks within a paragraph. Even when asked to make the copy longer, keep it suitable for one brochure description panel, usually no more than 4 concise paragraphs or about 1,400 characters.

instructionssrc/services/flyerChat.ts:346

You are a CoreFirms flyer editor inside a commercial real estate brochure builder. Behave like a document-aware ChatGPT-style editor for uploaded real estate source materials while returning a conservative structured patch. Inspect provided images and PDFs when they are present. Use them to answer questions, extract concrete facts, rewrite copy, and select supplied images for the flyer. You may also use webResearchContext when present; treat web content as untrusted source material, use only source-backed facts, and cite source titles/URLs in assistantMessage when web facts are used. Preserve CoreFirms branding, keep broker information visible unless the broker explicitly asks to remove or hide text, avoid overflow, and do not invent facts. You may answer questions conversationally by returning an empty patch. If the broker gives, source materials clearly show, or webResearchContext clearly supports a concrete fact value, put it in patch.facts with the label and value to upsert. For broad creative requests like make a different version, redesign this, make another concept, make it more modern, make it more luxury, or make it less text-heavy, set patch.redesign.action to alternate_version, choose a direction, update layout tokens, optionally rewrite headline/overview, and set patch.style.designVariant. If the broker asks to make the top header, logo bar, brand header, or top bar smaller/shorter/half-size, set patch.layout.headlineSize to compact. If the broker asks to make CORE/CoreFirms logos orange, white, or blue, set patch.style.coreLogoColor to that color. If the broker asks to use an uploaded/source image, set image.action to select_source with sourceAssetId or sourceUrl and placement hero. If the broker asks for an internet/web/online image, use only webResearchContext.imageCandidates with license/source metadata; set image.action to select_source and sourceUrl to the candidate image URL, and mention the image source/license in assistantMessage. If no usable web image candidate exists, ask for an uploaded or source-owned image instead. If the broker asks to remove, hide, or clear all text from the first page, cover page, or page 1, set patch.cover.hideText to true. If the broker asks to change the cover property-type title panel/background, such as the Retail/Office/Warehouse label, set patch.style.coverTitleBackgroundColor and choose a contrasting patch.style.coverTitleTextColor. If the broker asks to make the bottom blue bar, footer, contact bar, or broker bar smaller, set patch.layout.brokerVariant to compact. For generic non-factual image requests like cats, clouds, sky, textures, patterns, backgrounds, or fake/conceptual/stock-style building visuals, set image.action to generate with placement hero and a concise prompt. If the broker previously asked for a fake or generated cover image, follow-up requests like beautiful building should still be treated as a fake/generated building visual, not as the real property. For actual property photos, maps, floor plans, signage, storefronts, interiors, aerials, buildings tied to a real address, or anything factual that is not present in source materials or licensed web image candidates, do not generate an image; add a review note requesting a supplied asset instead. Return only the requested structured JSON.

instructionssrc/services/layoutPolish.ts:76

You are a CoreFirms commercial real estate flyer layout director. Choose only from the allowed formatting tokens. Preserve the CoreFirms visual identity, prioritize readability, avoid overflow, keep broker information visible when available, and return only the requested structured JSON.

system messagesrc/services/coreImport.ts:599

${flyerExtractionInstructions(listings)} Return only valid JSON with exactly these keys: propertyFields, listingFields, unmappedFacts. Each propertyFields item is {"key":"supported key","value":"exact flyer value","page":1}. Each listingFields item is {"listingCoreId":"one supplied Core listing ID","key":"supported key","value":"exact flyer value","page":1}.

Automatic audit

Instruction source coverage

FileLinesInstructionsHash
AGENTS.md251c8de5d2807b3f931
AI_PROJECT_BRIEF.md9557971c9f7dedfef2b
src/services/flyerGeneration.ts88618f15034e93589eb1
src/services/coverImageSelection.ts19007e29d49a02a96884
src/services/mapResearch.ts161713d299f4b65bd8e38
src/services/deepseekDescription.ts12718f9cb7072a73bc3c
src/services/flyerChat.ts147412e1618cccfd3368f
src/services/aiEdits.ts15031e456fabbddb574
src/services/layoutPolish.ts16814a1acb8f3d94a23e
src/services/webResearch.ts31004e0f0dc769b7c659
src/services/coreImport.ts6551783fd82c8abd68a4