docs(email): add Loops launch template plan#263
Conversation
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
|
Codex usage limits have been reached for code reviews. Please check with the admins of this repo to increase the limits by adding credits. |
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Greptile SummaryThis PR adds a Loops email-template planning artifact (
Confidence Score: 4/5Safe to merge. The only code change is a small improvement to an error-description utility; the rest is documentation. The catch block in shell/src/lib/error-boundary-utils.ts — the new catch arm should log before returning. Important Files Changed
Sequence DiagramsequenceDiagram
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
Prompt To Fix All With AIFix 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 |
| } catch (stringifyError) { | ||
| if (stringifyError instanceof globalThis.Error) { | ||
| return `Unstringifiable value (${stringifyError.name})`; | ||
| } | ||
| return typeof error; | ||
| } |
There was a problem hiding this 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.
| } 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!
Summary
specs/082-paid-beta-readiness/launch-email-templates.mdwith the launch-critical Loops email template inventory and implementation flow plan.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 --versionloops agent-context --output jsonloops auth status --output jsonloops api-key --output jsonloops transactional list --per-page 10 --output jsonloops contact-properties list --custom --output jsonloops lists list --output jsonrgdocumentation proof for all required template names, flow sections, checklist, gaps, and public docs pointergit diff --checkbun run typecheckbun run check:patternsbun run testNotes
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.