Skip to content

37 — TestimonialSubmission Model & Lifecycle (Blueprint §5.7)

Cursor-ready plan for implementing the core testimonial entity, state transitions, publication controls, consent fields, and data integrity rules.

37 — TestimonialSubmission Model & Lifecycle (Blueprint §5.7)

Section titled “37 — TestimonialSubmission Model & Lifecycle (Blueprint §5.7)”

Source: 02-Implementation-Blueprint.md§5.7 New model: TestimonialSubmission.

This document is a build spec only. No code changes are implied until a task references this file.

Related: 06 (public submit), 09/10 (moderation), 11 (published content), 08 (security/compliance), 20 (schema), 33 (moderation logs), 36 (media assets).


Implement TestimonialSubmission as the single source of truth for customer testimonial content, moderation status, publication state, product linkage, and consent evidence.


Blueprint §5.7 fields include:

  • identifiers: id, shopId, requestId
  • commerce links: shopifyOrderId, shopifyProductId, shopifyVariantId, shopifyCustomerId
  • identity: displayName, displayEmail
  • content: mediaType, headline, reviewText, rating
  • moderation: status (pending|approved|rejected|archived)
  • consent: consentAccepted, consentAcceptedAt, consentVersion
  • publication: published, publishedAt
  • merchandising: featured, sortOrder
  • timestamps: submittedAt, createdAt, updatedAt

Required indexes in blueprint:

  • @@index([shopId, status])
  • @@index([shopId, published])
  • @@index([requestId])
  • @@index([shopId, shopifyProductId, published])

  • Prisma model + migration
  • lifecycle/state transition contract
  • consent persistence contract
  • publication and merchandising fields
  • query/index guidance for downstream screens
  • media asset storage internals (covered by 36)
  • advanced AI scoring

  • pending
  • approved
  • rejected
  • archived
  • published=false (default)
  • published=true + publishedAt set

Publication should generally require status=approved.

Allowed:

  • pending -> approved|rejected|archived
  • approved -> archived
  • rejected -> pending (optional reopen)
  • archived -> pending|approved (optional restore)

Disallowed:

  • publish while status != approved (unless explicitly overridden and documented)

  • shopifyProductId is mandatory and comes from token-bound request by default.
  • use TestimonialProductLink for additional products (35).

Submission creation must ignore any client-attempted product override unless allowed by admin action in Screen 6.


Required at submit time:

  • consentAccepted=true
  • set consentAcceptedAt=now
  • copy consentVersion snapshot from Shop.testimonialConsentVersion

Never retroactively rewrite old submission consentVersion when merchant updates policy.


Server-side validation:

  • mediaType must be video|photo
  • rating nullable but if present must be 1..5
  • headline length cap (e.g. 255)
  • reviewText length cap (e.g. 5k)

PII caution:

  • displayEmail optional and should generally not be displayed publicly.

7) Creation flow contract (from public submit)

Section titled “7) Creation flow contract (from public submit)”

During api.testimonial-submit:

  1. validate token/request eligibility
  2. create submission row:
    • status=pending (or auto-approved by moderation settings)
    • published=false
    • submittedAt=now
  3. link media assets
  4. update request status to submitted

Use transaction for consistency.


8) Update flow contract (admin moderation/publishing)

Section titled “8) Update flow contract (admin moderation/publishing)”

Actions from Screen 6/7 can modify:

  • status
  • published, publishedAt
  • featured, sortOrder
  • optional content edits (headline, reviewText, rating)
  • product mapping (if merchant override allowed)

Every state-changing action should append TestimonialModerationLog entry (33).


Public API (05) should only return submissions where:

  • status='approved'
  • published=true
  • linked media asset status is ready
  • placement visibility conditions pass (from Screen 7 settings)

This predicate should be shared in one helper to avoid drift.


  • Inbox (09): filter by status, mediaType, submittedAt
  • Detail (10): load by id + shopId
  • Published (11): published=true and sort/visibility fields
  • Analytics (14/19): aggregate by submittedAt, mediaType, status, publishedAt

Use appropriate indexes and limited selects for list screens.


  • customer redact should remove/anonymize customer-linked fields:
    • displayEmail, maybe displayName depending policy
    • linkage to customer IDs where required by law
  • shop redact should remove all shop submissions via cascade

Align enforcement with 08-security-compliance-and-privacy.md.


  • Submission rows are created with correct defaults on public submit.
  • Moderation transitions follow allowed state rules.
  • Publish requires approved status (or documented exception).
  • Consent fields are always populated when consent accepted.
  • Public API returns only approved+published+ready rows.
  • Index-backed list queries perform under expected thresholds.

13) Suggested implementation order (for Cursor)

Section titled “13) Suggested implementation order (for Cursor)”
  1. Add Prisma model and migration for TestimonialSubmission.
  2. Add constants for status/media values.
  3. Implement submit creation flow and transaction.
  4. Implement moderation update actions with logs.
  5. Integrate storefront predicate and analytics consumers.
  6. Add compliance redaction handling for submission fields.

  • 02-Implementation-Blueprint.md — §5.7
  • 06-public-submission-page-screen-13.md
  • 10-submission-detail-screen-6.md
  • 11-published-content-screen-7.md
  • 05-storefront-widgets-and-read-api.md
  • 33-testimonial-moderation-log-model-blueprint-section-5-10.md

This folder already includes 05 through 36 plans. This file is 37-....