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-authgmail.send
scope ·PARSE+APPROVE <key>to publish Yossi's 08527 for real · flip
LEVSMS_TEST_MODE=falseto 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)
- 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). - 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?}. - AREA schedule storage — write an
AREA_<ZIP>(orSHUL_<CODE>-style) row so texters from that ZIP get the times. Capture submitter name + "what they call it" + the key on the row. - Sam-visible review — render the prepared result (a preview Sam SEES) before publish; one-tap confirm OR auto-confirm if confidence ≥ threshold.
- 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.
- Deploy + verify —
clasp push+clasp deploy -i <LevSMS LIVE_ID>(the self-deploy capability — seefeedback_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)
- Deploy the on-disk code (KS/YOMI committed past @73 but maybe not live) before assuming those commands answer.
- Enable clasp run / API-executable on LevSMS so a session can trigger PARSE/ADDZIP/etc. from the CLI (currently can't).
- Retention is THE constraint (Gate 1: 10 returning NW users, unmet) — the email-intake is expansion; don't let it eclipse the 24h-after-opt-in "today's TIMES" retention experiment.
- Generalization:
project_per_client_attachment_inbox(per-entity inbox → parse → back-link → cadence; LevSMS = v0.1).
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.