Link previews no longer submit answers. Failed queue attempts retain reusable drafts; recipient queues prevent duplicate retries. Tested: 18 isolated regressions pass; 13 fail on baseline. AI-Agent: Codex (OpenAI) AI-Session: 01a14186-e2ee-77b1-9526-a32c58cd6a00 |
||
|---|---|---|
| design | ||
| ops | ||
| public | ||
| views | ||
| README.md | ||
| site.config.js | ||
The Founder's Break
Event site for the weekly UF startup pool social at Silver Q, Gainesville. Hosted by OneTouch Studio at https://foundersbreak.onetouch.studio.
Corben EJS site. There is no build step: deploy_site copies this repo verbatim.
site.config.js all schedule, venue and copy data
views/index.ejs the single page, including the next-date logic
public/css/style.css brand tokens lifted from onetouch.studio
public/js/main.js nav state, scroll reveal, live countdown, RSVP submit
public/assets/ the recurring .ics and the print-resolution QR
public/images/ logo, favicon, RSVP QR
The date is never hard-coded
views/index.ejs computes the next occurrence at render time from weekday +
firstEvent in site.config.js. public/js/main.js runs the same rule in the
browser, so the countdown ticks and rolls itself over to the following Thursday
once the night ends, with no reload and no redeploy.
Cancelling a week
Add the ISO date to cancelled in site.config.js and redeploy:
cancelled: ["2026-11-26"], // Thanksgiving
The page then skips that Thursday, shows a notice naming the cancelled date, and points at the next one that is actually happening. Two other places do not read this list and need the same date added by hand:
public/assets/founders-break.icsis a plain weekly recurrence.- the
CANCELLEDarray in thefounders-break-reminderCorben function, or it will email everyone about a Break that is not happening.
Deploying
deploy_site id=4ae63819-3f63-4fea-b0de-eb1e5befe607 repo=gator/founders-break mode=ejs
Nothing redeploys itself. Every edit needs the deploy step.
What happens when somebody RSVPs
The form POSTs to /forms/rsvp. The Corben sites renderer stores it in
site_form_submissions and fires a form.submitted event. From there it is all
plain JavaScript, deliberately: there is no LLM anywhere in the send path.
| Piece | Id |
|---|---|
Trigger Founder's Break RSVP (form.submitted, form_name=rsvp) |
e035fb68-2ecd-452b-bf74-a051c2775423 |
Function founders-break-rsvp |
3d3c4bed-c073-40a4-9a32-9ff6944737dd |
Function founders-break-reminder |
cf2a1e4c-43f9-4cf4-8fc5-652c494940ae |
Cron Founder's Break weekly reminder (every 6h) |
84efd815-5426-4952-af88-2f4ed81efc54 |
Email list Founder's Break |
09429866-0005-4260-bf56-2d520eea4db7 |
founders-break-rsvp resolves a CRM contact, writes the email address back onto
it (create_contact dedupes server-side and can return a record with no address,
which would put an unmailable member on the list), adds them to the list, sends
the HTML confirmation, and sends an internal note to gatordevin@gmail.com.
founders-break-reminder runs every 6 hours, works out the Eastern weekday
itself, and only sends on Wednesday between 09:00 and 20:00 ET. A kv marker
keyed to the event date makes any repeat run a no-op, which is what makes it safe
to run four times a day.
Both functions authenticate with a scoped email + crm key held by the paused
Founders Break agent record (666f7ad0-f546-4fc6-a3c4-14f1384e91a5). That
agent runs nothing. It exists only because an API key cannot mint another API
key. Deleting it revokes the key and breaks both functions.
Sharing
/flyer (views/flyer.ejs) is the link to send someone: the poster shown as a
sheet, a download button, and the share controls. It is what the footer and the
post-RSVP confirmation both point at, rather than the raw PDF, so a share always
lands on something with context.
There is no general "share protocol". What exists is the Web Share API
(navigator.share), which on a phone opens the real OS share sheet and is the
only way to reach apps a web page cannot link to. Where navigator.canShare
allows files it shares the PDF itself, which is far better in a group chat than
a link; it falls back to sharing the URL, and the button stays hidden entirely on
browsers without support. Under that sit plain per-channel URLs (SMS, mailto,
WhatsApp, X, LinkedIn, Facebook) that work with no JavaScript at all, and a
copy-to-clipboard with a select-the-text fallback for in-app browsers that block
the clipboard.
site.config.js -> images.share is a 1200x630 unfurl card carrying the same four
facts the flyer leads with. Both / and /flyer use it for og:image, so a
pasted link never previews as a wordless photo. Regenerate it from
design/og-card.html the same way as the flyer.
The printable flyer
public/assets/founders-break-flyer.pdf is a one-page US Letter flyer, linked
from the footer and from the confirmation someone sees right after they RSVP,
which is the moment they might think about bringing people. The source that
generates it lives in design/ with regeneration instructions; edit that, do not
re-design the PDF.
The QR code
public/images/rsvp-qr.png and public/assets/rsvp-qr.svg both encode
https://foundersbreak.onetouch.studio/#rsvp. Deliberately dark-on-white:
inverted QR codes are unreliable on older scanners and these go on flyers.
Regenerate with the qrcode npm package if the URL ever changes.
Tracking
The Corben tracker is injected into every EJS site automatically, so pageviews
(with UTM, referrer, device, browser), clicks on any a/button/[data-track],
form_start, form_submit, scroll depth and engagement time all land with no
work from us. Key elements carry a data-track label so the click rows are
readable: rsvp-nav, rsvp-hero, rsvp-submit, add-to-calendar, venue-map,
details-map, venue-instagram, hero-what-to-expect.
public/js/main.js adds the three steps the tracker cannot see on its own, via
the same public /analytics/events endpoint:
| Event | Means |
|---|---|
rsvp_reached |
scrolled the RSVP block into view |
rsvp_completed |
submission stored, event_data.via is form or reminder-email |
rsvp_failed |
submission rejected, with the reason |
So "reached but did not finish" is rsvp_reached minus rsvp_completed, and
"started typing then left" is form_start minus form_submit.
Emails carry a 1x1 open pixel at GET /analytics/open.gif?s=<site>&k=<kind>&e=<email>&d=<date>,
landing in site_events as email_open with kind confirm-first,
confirm-returning or reminder. Apple Mail Privacy Protection and Gmail's
image proxy prefetch images, so treat opens as a ceiling rather than a headcount.
Read any of it with get_site_analytics, get_site_events or get_site_click_map.