40 — TestimonialTemplate Model & Resolution Rules (Blueprint §5.4)
Cursor-ready plan for template data model, fallback resolution, channel-specific validation, and send-time rendering contracts.
40 — TestimonialTemplate Model & Resolution Rules (Blueprint §5.4)
Section titled “40 — TestimonialTemplate Model & Resolution Rules (Blueprint §5.4)”Source: 02-Implementation-Blueprint.md — §5.4 New model: TestimonialTemplate.
This document is a build spec only. No code changes are implied until a task references this file.
Related: 16 (templates screen), 07/32 (delivery workflow), 39 (request model), 20 (schema migration standards).
0) Goal (one sentence)
Section titled “0) Goal (one sentence)”Implement TestimonialTemplate as the single source of truth for outreach message content with deterministic fallback rules for campaign/default/channel/template type combinations.
1) Blueprint model recap
Section titled “1) Blueprint model recap”Blueprint §5.4 fields:
idshopIdcampaignId(nullable)channel(email|sms)templateType(initial_request|reminder_1|reminder_2)subject(email only)bodyisDefaultcreatedAtupdatedAt
Unique:
@@unique([shopId, campaignId, channel, templateType])
2) Scope boundaries
Section titled “2) Scope boundaries”In scope
Section titled “In scope”- model + migration
- save/update validation rules
- runtime template resolution algorithm
- rendering token contract and fallback behavior
Out of scope
Section titled “Out of scope”- rich drag-drop email builder
- multilingual template variants per locale (future extension)
3) Canonical dimensions
Section titled “3) Canonical dimensions”A template is uniquely identified by:
- tenant:
shopId - optional campaign scope:
campaignId|null - channel:
email|sms - message stage:
initial_request|reminder_1|reminder_2
No other hidden dimensions should affect resolution in v1.
4) Resolution order (critical runtime contract)
Section titled “4) Resolution order (critical runtime contract)”When sending a message for (shopId, campaignId, channel, templateType):
- campaign-specific template (
campaignId = requested campaign) - shop default template (
campaignId = nullandisDefault = true) - hardcoded system fallback (last resort)
This exact order must be reused in:
- send worker
- send-test action
- preview rendering
Do not allow each surface to implement different resolution logic.
5) Validation rules
Section titled “5) Validation rules”5.1 Common
Section titled “5.1 Common”bodyrequired and non-emptychannelin allowed settemplateTypein allowed set
5.2 Email-specific
Section titled “5.2 Email-specific”subjectrequired whenchannel=email- recommended subject max length (e.g. 150 chars)
5.3 SMS-specific
Section titled “5.3 SMS-specific”subjectignored or null (do not persist stale subject semantics)- body length warning threshold (soft warning, not hard fail unless product policy requires)
5.4 Default semantics
Section titled “5.4 Default semantics”- For each
(shopId, channel, templateType), only one row should be considered default fallback. - Enforce via app logic when toggling
isDefault:- setting one row default should unset sibling defaults of same tuple.
6) Token rendering contract
Section titled “6) Token rendering contract”Supported variables from blueprint screen 4:
{{customer_first_name}}{{shop_name}}{{product_name}}{{submission_link}}{{incentive_text}}
6.1 Renderer behavior
Section titled “6.1 Renderer behavior”- Unknown tokens:
- keep literal and return warning in preview/send-test output.
- Missing values:
- apply safe fallback (e.g., empty string or friendly placeholder).
- Strip repeated blank lines after replacement for readable output.
6.2 Security
Section titled “6.2 Security”- Escape/render output according to destination channel (email html/plain rules if needed).
- Never inject untrusted raw HTML in SMS flow.
7) Write-path contracts
Section titled “7) Write-path contracts”7.1 Save/upsert
Section titled “7.1 Save/upsert”Use upsert keyed on unique tuple:
(shopId, campaignId, channel, templateType)
7.2 Duplicate handling
Section titled “7.2 Duplicate handling”If unique conflict occurs in concurrent edits:
- retry with latest row merge strategy or return conflict message.
7.3 Audit
Section titled “7.3 Audit”Optional: add template change audit table later (not required by blueprint).
8) Integration contracts
Section titled “8) Integration contracts”8.1 Campaign engine/delivery
Section titled “8.1 Campaign engine/delivery”TestimonialCampaign.channel dictates which template channel must resolve.
8.2 Reminder sends
Section titled “8.2 Reminder sends”reminder_1andreminder_2types used according toreminderCount.
8.3 Requests log troubleshooting
Section titled “8.3 Requests log troubleshooting”When send fails due to template resolution, error should include:
- missing tuple details (
channel,templateType,campaignId) - not raw template contents
9) Migration guidance
Section titled “9) Migration guidance”Migration rollout:
- add model
- seed minimal default templates per channel/type for active shops (optional but recommended)
- deploy templates UI and sender resolution in same release window
Seeding reduces runtime “no template found” failures.
10) Query patterns
Section titled “10) Query patterns”Common reads:
- fetch campaign-specific + default candidates for tuple
- list all templates for shop in templates screen grouped by channel/type/scope
Potential index optimization:
@@index([shopId, channel, templateType])if lookup frequency becomes high.
11) Acceptance criteria
Section titled “11) Acceptance criteria”- Upsert works for all channel/type combinations.
- Resolution order behaves exactly as defined in section 4.
- Email requires subject; SMS ignores subject.
- Token rendering supports all blueprint variables.
- Missing campaign-specific template falls back to shop default, then system fallback.
12) Suggested implementation order (for Cursor)
Section titled “12) Suggested implementation order (for Cursor)”- Add Prisma model and migration.
- Implement template resolver helper (shared).
- Implement renderer/token replacement helper.
- Wire save/upsert in templates screen.
- Wire sender worker to resolver helper.
- Add send-test and preview validation parity.
13) References
Section titled “13) References”02-Implementation-Blueprint.md— §5.4 and Screen 4 fields16-templates-screen-4.md07-email-sms-request-delivery-pipeline.md32-email-sms-operational-detail-blueprint-section-4.md
14) Note on numbering
Section titled “14) Note on numbering”This folder already includes 05 through 39 plans. This file is 40-....