בס״ד

LevSMS Email-Intake Pipeline — BUILD SPEC (Sam's requirement, locked 2026-06-04)

docs/LEVSMS_EMAIL_INTAKE_SPEC.md · last changed (pre-VM history) · rendered from GitHub master

LevSMS Email-Intake Pipeline — BUILD SPEC (Sam's requirement, locked 2026-06-04)

✅ BUILT + DEPLOYED 2026-06-04 — LevSMS @79, commit c540408 (zee78900/levsms).
All 6 steps shipped + tested (standing parser 11/11, routing 6/6, NW unbroken). The
functions, the live status, and the 3 remaining Sam-side steps (re-auth gmail.send
scope · PARSE+APPROVE <key> to publish Yossi's 08527 for real · flip
LEVSMS_TEST_MODE=false to email real submitters) are recorded in memory
project_levsms_email_intake. The build sections below are kept as the design record.

Why this exists: Sam doesn't want to re-explain this every session. This is the
full, end-to-end requirement for the "someone emails a schedule → it's parsed,
published, and the sender gets confirmed it's officially in" loop. Build it from this;
don't re-ask Sam. Topic-preload trigger: any LevSMS / support@levsms.com / schedule-intake work.

The goal (Sam's words, 2026-06-04)

A community member emails a schedule to support@levsms.com. The system must:
1. Receive it (auto — no manual fishing).
2. Parse it into structure.
3. Prepare it (the right SHUL/AREA format).
4. Publish it (now served when people text from that ZIP).
5. Confirm back to the SENDER — reply to their email: "Got it — [name] ([ZIP]) is now live. Key/code: [KEY]." So the submitter (and/or Sam) sees a response and knows it's officially there, with the code/key + name as the attribution.

Sam wants to SEE the prepared result before it goes out, and the sender to get a confirmation reply. Either Sam confirms, or it auto-confirms (high-confidence), and the confirmer is attributed by a key + a name (what they call it).

The test case already in hand (build against this)

Yossi Treitel → support@levsms.com, "Shul schedule" (Tue 2026-06-02), in sam@hookstreetcapital.com inbox. A standing davening schedule for "Jackson 21," a neighborhood in Jackson NJ, ZIP 08527 — Shacharis (Sun + Mon–Fri), Mincha, Maariv, by minyan/location (Avalon / Mantoloking). NOTE: it's a standing neighborhood multi-minyan list with NO parsha/week — the current parser is single-shul + weekly, so it does NOT fit. 08527 is the #1 requested ZIP (12 requests) — real demand. The full Maariv lines were cut off in Sam's screenshot; get the complete text from the email when building.

Current state vs. the requirement (~70% built — gaps below)

Step Built? Gap to close
Receive (auto) ⚠️ Gmail filter to:support@levsms.com → label LevSMS-Inbox is NOT set up → emails don't auto-flow. (Sam-side Gmail setting, or a Worker/Apps-Script poll of the inbox.)
Parse ⚠️ Parser (parseScheduleText_) handles single-shul + parsha only. Must also handle standing neighborhood/multi-minyan schedules with no parsha (Yossi's case).
Prepare ⚠️ Writes SHUL_<CODE> rows. Needs an AREA-schedule path (by ZIP) for neighborhood submissions, so 08527 texters get these times.
Publish APPROVE → served. (But community-wide aggregator still half-built, open loop #27.)
Confirm to SENDER Missing entirely. Today only Sam gets an admin SMS preview. Need a reply email to the submitter: "received → queued → now live," with the key + name.
Attribution (key + name) ⚠️ The 8-char FileId is "the key"; confirmer = phone only. Capture the submitter's name + what they call the shul/area and echo it.
Auto-confirm tier All approval is manual (Sam texts APPROVE). Add a confidence-gated auto-publish for high-certainty parses; low-certainty queues.
"Queued → notified" No state-machine message back to the submitter on queue and on publish.

The build punch-list (do these, in order)

  1. Gmail intake — set the filter (Sam) OR poll support@levsms.com from the router; drain body+attachments into the Drive intake folder (levSmsGmailToInbox_ already drains a labeled inbox — just needs the label flowing).
  2. Parser v2 — extend parseScheduleText_ to recognize standing/neighborhood schedules (no parsha; multiple minyanim by location/time; Shacharis/Mincha/Maariv buckets). Output: {name, area, zip, type:'standing'|'weekly', shacharis[], mincha[], maariv[], shabbos?}.
  3. AREA schedule storage — write an AREA_<ZIP> (or SHUL_<CODE>-style) row so texters from that ZIP get the times. Capture submitter name + "what they call it" + the key on the row.
  4. Sam-visible review — render the prepared result (a preview Sam SEES) before publish; one-tap confirm OR auto-confirm if confidence ≥ threshold.
  5. Confirm-to-sender — on publish, send a reply email to the submitter: "Your schedule for [name] ([ZIP]) is now live on LevSMS. Reference key: [KEY]." (And a "received, queued for review" auto-reply on intake.) Attribute the confirmer by key + name.
  6. Deploy + verifyclasp push + clasp deploy -i <LevSMS LIVE_ID> (the self-deploy capability — see feedback_clasp_redeploy_breaks_webapp), then test the Yossi 08527 case end-to-end: email → parse → see preview → confirm → sender gets the reply → text from 08527 returns the times.

Also remember (related LevSMS open loops)

Canonical LevSMS refs: levsms/router/Code.js, levsms/SYSTEM_STATE.md, docs/MALCA_YENTA_CONTEXT.md. Backend sheet 1E4_3BJaqpTlNyF3xdJetVb8sYp-9RfqNFPKUanffxhA; drop folder 1DFVlv5kXxLeVYo9IjKID6iOLDMmlB4Zq; LevSMS script 1KEr8Lt3YS-5FkkZJYGlGqb2qN_yR029_MAQe5BhlrnIcZPSuNYsKcICn.

Source trail · docs/LEVSMS_EMAIL_INTAKE_SPEC.md @ master · rendered 2026-07-02 7:23 PM EDT by scripts/build-docs.py · the .md in the repo is the truth; this page is the phone-readable view