Skip to content

docs(email): add Loops launch template plan#263

Open
HamedMP wants to merge 2 commits into
mainfrom
hamed-mat-41-templates-for-emails
Open

docs(email): add Loops launch template plan#263
HamedMP wants to merge 2 commits into
mainfrom
hamed-mat-41-templates-for-emails

Conversation

@HamedMP
Copy link
Copy Markdown
Owner

@HamedMP HamedMP commented May 30, 2026

Summary

  • Add specs/082-paid-beta-readiness/launch-email-templates.md with the launch-critical Loops email template inventory and implementation flow plan.
  • Map Clerk, Stripe/billing, and Matrix OS app/backend events to Loops event names, template names, required fields, idempotency keys, and non-blocking failure behavior.
  • Add a public launch-readiness docs pointer to the detailed email plan.
  • Fix one pre-existing bare catch so the required pattern scanner can pass.

Template And Event Inventory

Templates: matrix_account_welcome_v1, matrix_auth_product_prompt_v1, matrix_beta_activated_v1, matrix_billing_succeeded_v1, matrix_billing_payment_failed_v1, matrix_subscription_changed_v1, matrix_subscription_canceled_v1, matrix_cli_activation_nudge_v1, matrix_cloud_coding_nudge_v1, matrix_admin_launch_alert_v1.

Events: matrix.account.created.v1, matrix.auth.product_prompt.v1, matrix.beta.activated.v1, matrix.billing.succeeded.v1, matrix.billing.payment_failed.v1, matrix.billing.subscription_changed.v1, matrix.billing.subscription_canceled.v1, matrix.activation.cli_nudge.v1, matrix.activation.cloud_coding_nudge.v1, matrix.admin.launch_alert.v1.

Tests

  • loops --version
  • loops agent-context --output json
  • loops auth status --output json
  • loops api-key --output json
  • loops transactional list --per-page 10 --output json
  • loops contact-properties list --custom --output json
  • loops lists list --output json
  • targeted rg documentation proof for all required template names, flow sections, checklist, gaps, and public docs pointer
  • git diff --check
  • bun run typecheck
  • bun run check:patterns
  • bun run test

Notes

No real customer emails were sent. The Loops CLI validation was read-only and confirmed Matrix OS team access plus no published transactional emails yet.

HamedMP added 2 commits May 30, 2026 03:00
Summary:
- Replace a bare catch in error-boundary utilities with explicit error-type handling.

Rationale:
- The required pattern scanner treats bare catches as violations, and this keeps the fallback safe without changing normal error formatting.

Tests:
- bun run typecheck
- bun run check:patterns
- bun run test
Summary:
- Add the launch email template inventory for Matrix OS product emails through Loops.
- Map Clerk, Stripe/billing, and Matrix app/backend events to Loops events, templates, payload fields, idempotency, and failure behavior.
- Add a public launch-readiness docs pointer to the detailed internal plan.

Rationale:
- MAT-41 is planning/template work for launch-critical email quality, billing trust, and support load without introducing another provider or sending customer emails.

Tests:
- targeted rg documentation proof
- loops read-only CLI validation
- bun run typecheck
- bun run check:patterns
- bun run test
@linear-code
Copy link
Copy Markdown

linear-code Bot commented May 30, 2026

MAT-41

@chatgpt-codex-connector
Copy link
Copy Markdown

Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits.
Repo admins can enable using credits for code reviews in their settings.

@vercel
Copy link
Copy Markdown

vercel Bot commented May 30, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
matrix-os-www Ready Ready Preview, Comment May 30, 2026 1:02am

Request Review

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 30, 2026

Greptile Summary

This PR adds a Loops email-template planning artifact (specs/082-paid-beta-readiness/launch-email-templates.md), a pointer to it in the public launch-readiness docs, and a pre-existing pattern-scanner fix in error-boundary-utils.ts.

  • Spec document maps all ten launch-critical email templates to Loops event names, required contact properties, idempotency keys, and non-blocking failure policy. Open product decisions (billing source of truth, verification ownership, sender identity, frequency caps) are explicitly deferred with follow-up checklist items.
  • error-boundary-utils.ts promotes the bare catch {} to a typed catch with a better fallback string, though the caught error is not logged.
  • onboarding-launch-readiness.mdx adds a short prose section linking to the spec.

Confidence Score: 4/5

Safe to merge. The only code change is a small improvement to an error-description utility; the rest is documentation.

The catch block in error-boundary-utils.ts still swallows stringifyError without logging it, so this extremely rare failure path remains silent in production. Everything else in the PR is planning documentation with no runtime impact.

shell/src/lib/error-boundary-utils.ts — the new catch arm should log before returning.

Important Files Changed

Filename Overview
shell/src/lib/error-boundary-utils.ts Fixes bare catch {} by binding stringifyError and checking its type, but the caught error is never logged.
specs/082-paid-beta-readiness/launch-email-templates.md New planning artifact for Loops email templates; all open decisions explicitly deferred.
www/content/docs/onboarding-launch-readiness.mdx Adds launch email templates section with pointer to the spec.

Sequence Diagram

sequenceDiagram
    participant Clerk
    participant Stripe
    participant MatrixBackend as Matrix Backend
    participant LoopsWorker as Loops Worker (queued)
    participant Loops
    participant User

    Clerk->>MatrixBackend: webhook user.created
    MatrixBackend->>MatrixBackend: persist user (source of truth)
    MatrixBackend->>LoopsWorker: enqueue upsert + emit matrix.account.created.v1
    LoopsWorker->>Loops: "upsert contact (idempotency: clerk:{event.id}:welcome_v1)"
    Loops-->>User: matrix_account_welcome_v1 email

    Stripe->>MatrixBackend: webhook checkout.session.completed
    MatrixBackend->>MatrixBackend: persist entitlement
    MatrixBackend->>LoopsWorker: enqueue emit matrix.beta.activated.v1
    LoopsWorker->>Loops: "send event (idempotency: stripe:{event.id}:beta_active_v1)"
    Loops-->>User: matrix_beta_activated_v1 email

    Stripe->>MatrixBackend: webhook invoice.payment_failed
    MatrixBackend->>MatrixBackend: update billing state
    MatrixBackend->>LoopsWorker: enqueue emit matrix.billing.payment_failed.v1
    LoopsWorker->>Loops: "send event (idempotency: stripe:{event.id}:pay_failed_v1)"
    Loops-->>User: matrix_billing_payment_failed_v1 email

    MatrixBackend->>MatrixBackend: detect CLI not activated (timer/check)
    MatrixBackend->>LoopsWorker: enqueue emit matrix.activation.cli_nudge.v1 (freq-capped)
    LoopsWorker->>LoopsWorker: "suppress if matrixCliActivated == true"
    LoopsWorker->>Loops: "send event (idempotency: matrix:{eventId}:cli_nudge_v1)"
    Loops-->>User: matrix_cli_activation_nudge_v1 email
Loading
Prompt To Fix All With AI
Fix the following 1 code review issue. Work through them one at a time, proposing concise fixes.

---

### Issue 1 of 1
shell/src/lib/error-boundary-utils.ts:19-24
The caught `stringifyError` is never logged. CLAUDE.md's error-handling rule states every catch must "at minimum, log the error." Because `String(error)` throwing is a genuine (if rare) failure signal, silently swallowing it makes this class of pathological values invisible in production traces.

```suggestion
  } catch (stringifyError) {
    if (stringifyError instanceof globalThis.Error) {
      console.error("describeUnknownError: String() threw", stringifyError);
      return `Unstringifiable value (${stringifyError.name})`;
    }
    console.error("describeUnknownError: String() threw non-Error", stringifyError);
    return typeof error;
  }
```

Reviews (1): Last reviewed commit: "docs(email): add Loops launch template p..." | Re-trigger Greptile

Comment on lines +19 to 24
} catch (stringifyError) {
if (stringifyError instanceof globalThis.Error) {
return `Unstringifiable value (${stringifyError.name})`;
}
return typeof error;
}
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 The caught stringifyError is never logged. CLAUDE.md's error-handling rule states every catch must "at minimum, log the error." Because String(error) throwing is a genuine (if rare) failure signal, silently swallowing it makes this class of pathological values invisible in production traces.

Suggested change
} catch (stringifyError) {
if (stringifyError instanceof globalThis.Error) {
return `Unstringifiable value (${stringifyError.name})`;
}
return typeof error;
}
} catch (stringifyError) {
if (stringifyError instanceof globalThis.Error) {
console.error("describeUnknownError: String() threw", stringifyError);
return `Unstringifiable value (${stringifyError.name})`;
}
console.error("describeUnknownError: String() threw non-Error", stringifyError);
return typeof error;
}

Context Used: CLAUDE.md (source)

Prompt To Fix With AI
This is a comment left during a code review.
Path: shell/src/lib/error-boundary-utils.ts
Line: 19-24

Comment:
The caught `stringifyError` is never logged. CLAUDE.md's error-handling rule states every catch must "at minimum, log the error." Because `String(error)` throwing is a genuine (if rare) failure signal, silently swallowing it makes this class of pathological values invisible in production traces.

```suggestion
  } catch (stringifyError) {
    if (stringifyError instanceof globalThis.Error) {
      console.error("describeUnknownError: String() threw", stringifyError);
      return `Unstringifiable value (${stringifyError.name})`;
    }
    console.error("describeUnknownError: String() threw non-Error", stringifyError);
    return typeof error;
  }
```

**Context Used:** CLAUDE.md ([source](https://app.greptile.com/finna/github/hamedmp/matrix-os/-/custom-context?memory=7363726a-a178-4b9b-a535-bc60be181a1a))

How can I resolve this? If you propose a fix, please make it concise.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant