MotirBuilding in public
MOTIR · moooon
onMotir
You’re viewing a public project. Anyone can view it — no account needed. Sign in to submit, upvote, or comment on requests.View-only — you can’t edit work items
MOTIR-339

6.2.5 Filter subscriptions — schedule schema + Inngest cron delivery (results email, 50-row cap, deep link) + subscribe/unsubscribe UI

Done
Description

Estimate: 32m · Depends on: 6.2.1, 6.2.2, 6.2.4

The emailed-results loop, on the Story 1.6 jobs substrate.

Schema: saved_filter_subscription — filter FK (cascade per 6.2.1's delete), user FK, schedule preset (daily | weekdays | weekly(day)) + hour (workspace-timezone semantics documented), unique per (filter, user).

Delivery: an Inngest cron job (the dailyHealthCheck precedent) ticking hourly → due subscriptions fan out as events (one delivery per subscription — retries/DLQ ride the shipped 1.6 machinery); each delivery resolves the filter via the 6.2.1 read AS THE SUBSCRIBER (the permission matrix applies — a subscriber who lost browse access gets no mail, and a filter gone private stops delivering to non-owners), renders lib/emailTemplates/filterSubscription.tsx — filter name, first 50 results (our page unit; Jira caps at 200) as identifier + title + status, the total count, a deep link to the applied ?filter=v1: URL, and an unsubscribe link — and sends via lib/email.ts. Zero-result deliveries still send (the mirror's subscription is a report, not an alert; note it in the template copy).

UI: the subscribe control + preset editor per the 6.2.2 design (dropdown row action + directory row action), the subscribed state, unsubscribe (in-app + the email link, token-authenticated like the shipped unsubscribe patterns). Strings via next-intl.

Acceptance criteria

  • Schedule semantics tested with a frozen clock: daily/weekdays/weekly(day) at the configured hour each compute due-ness correctly across DST boundaries (documented timezone rule).
  • The delivery resolves as the subscriber (permission-loss and gone-private paths asserted: no mail), caps at 50 + true total count, deep-links to the exact applied state, and survives a stale-referent AST (the email renders the degraded condition, never crashes the job — DLQ stays empty in the happy path).
  • Template snapshot-tested (subject + text + html; unsubscribe link verbatim in plain text per the email-template contract); subscribe/unsubscribe round-trips from both mounts; filter deletion cascades subscriptions.
  • pnpm test:coverage ≥90% on the new service/job files.

Context refs

  • lib/jobs/ (defineJob/registry/retries/DLQ) + lib/jobs/definitions/dailyHealthCheck.ts — the cron precedent
  • lib/emailTemplates/ contract in motir-core/CLAUDE.md (pure templates, hand-written plain text) + lib/email.ts
  • 6.2.1 resolve-as-user read; design/work-items/saved-filters.mock.html (the subscription editor panels)
  • The verified Jira subscription facts (presets/cron, 200-cap) in the Story 6.2 description
Status
Done
Type
Sub-task