Intent: the property page (/homes/:id) says "Open house: Sat …" as plain text; a buyer who means to come should be able to put it in their calendar without retyping it.
Done looks like:
[ ] When an active listing has an open house, the property page's open house line has an "Add to calendar" link to /homes/:id/open-house.ics, server-rendered, no JavaScript.
[ ] That route answers text/calendar; charset=utf-8 with content-disposition: attachment, one VEVENT per open house: the address as LOCATION, "Open house: <address>" as SUMMARY, the start and end times, and the agent's name and phone in DESCRIPTION. It reuses the iCalendar writing (fold, CRLF lines, escaping) already in src/schedule.ts rather than a second writer.
[ ] It shows only what the property page already shows a stranger: read is anon through permit, the same as /homes/:id; a withdrawn, expired or sold listing, one with no open house, or an unknown id is a 404 with no body text from the listing.
[ ] Typed text that contains ;, ,, a newline or \ is escaped per RFC 5545, and a test with a hostile address proves it (extend src/hostile.test.ts).
[ ] Tests: the link is present only with an open house on an active listing; the file has one VEVENT per open house with the right times; the refusals above.
[ ] The README walkthrough step about the property page mentions the link.
Intent: the property page (
/homes/:id) says "Open house: Sat …" as plain text; a buyer who means to come should be able to put it in their calendar without retyping it.Done looks like:
/homes/:id/open-house.ics, server-rendered, no JavaScript.text/calendar; charset=utf-8withcontent-disposition: attachment, one VEVENT per open house: the address as LOCATION, "Open house: <address>" as SUMMARY, the start and end times, and the agent's name and phone in DESCRIPTION. It reuses the iCalendar writing (fold, CRLF lines, escaping) already insrc/schedule.tsrather than a second writer.permit, the same as/homes/:id; a withdrawn, expired or sold listing, one with no open house, or an unknown id is a 404 with no body text from the listing.;,,, a newline or\is escaped per RFC 5545, and a test with a hostile address proves it (extendsrc/hostile.test.ts).Files likely:
src/schedule.ts(anopenHouseCalendar(l)),src/routes.ts,src/pages/property.ts, tests beside them,README.md.Risks: time zones — write the same local-time form the agent's
/listings/:id/schedule.icsalready uses so both agree. Do not loosen any contract cell.Suggested crew: builder.