Skip to main content

QA Guide

How to verify Syntropy-Journals features against a seeded local development environment.

Prerequisites

RequirementCheckInstall
Dockerdocker --versiondocker.com
Python 3.11+python3 --versionVia pyenv or system
uvuv --versioncurl -LsSf https://astral.sh/uv/install.sh | sh
PostgreSQL client (optional)psql --versionapt install postgresql-client
Integration setup: Before running a full demo, complete the Integration Setup Guide to configure Clerk, Stripe, and optional services.

Environment Setup

# 1. Install dependencies
make install

# 2. Create env file from template (if first time)
make env-dev

# 3. Start PostgreSQL
make docker-db

# 4. Run migrations
make db-migrate

# 5. Seed demo data
make run seed
Expected seed output — all loaders report loaded or skipped counts:
Seeding Syntropy-Journals Database
=======================================================
  + user: 1 loaded, 0 skipped (id=1)
  + partner_user: 1 loaded, 0 skipped (id=2)
  + checkins: 8 loaded, 0 skipped
  + medications: 4 loaded, 0 skipped
  + food_logs: 4 loaded, 0 skipped
  + symptoms: 3 loaded, 0 skipped
  + profile: 1 loaded, 0 skipped
  + health_profile: 1 loaded, 0 skipped
  + settings: 1 loaded, 0 skipped
  + products: 4 loaded, 0 skipped
  + protocols: 4 loaded, 0 skipped
  + user_protocols: 2 loaded, 0 skipped
  + protocol_checkins: 28 loaded, 0 skipped
=======================================================
Seeding complete: 61 loaded, 0 skipped

Starting the Dev Server

# Standard dev (requires LLM API keys in envs/dev)
make run dev

# Mock LLM mode (no API keys needed — for UI/data QA)
make run dev-mock
The app starts at http://localhost:3000 (frontend) with the Reflex backend on port 8000.

Full Reset (Clean Slate)

When you need a pristine environment:
make db-reset      # Drop and recreate the database
make db-migrate    # Re-run all Alembic migrations
make run seed      # Re-seed demo data
make run dev           # Start the app

QA Checklist: Seed Data Migration

Verify that the Alex Rivera → Sarah Chen migration is complete and all demo data renders correctly.

1. Database Verification (CLI)

Run before starting the app to confirm seed data is present:
# Verify demo users exist
PGPASSWORD=postgres psql -h localhost -U postgres -d syntropy_journals -c "
  SELECT id, email, first_name, last_name, user_type
  FROM \"user\"
  WHERE email LIKE '%test.syntropyhealth%'
  ORDER BY id;
"
Expected: Two rows:
idemailfirst_namelast_nameuser_type
Nsarah@test.syntropyhealth.bioSarahChenUSER
Msarah+shopify@test.syntropyhealth.bioSarahChenUSER
# Verify record counts
PGPASSWORD=postgres psql -h localhost -U postgres -d syntropy_journals -c "
  SELECT 'checkins' as entity, COUNT(*) FROM checkins WHERE checkin_id LIKE 'CHK-seed%'
  UNION ALL
  SELECT 'medications', COUNT(*) FROM medication_entries
  UNION ALL
  SELECT 'food_logs', COUNT(*) FROM food_log_entries
  UNION ALL
  SELECT 'symptoms', COUNT(*) FROM symptom_entries
  UNION ALL
  SELECT 'profiles', COUNT(*) FROM userprofile
  UNION ALL
  SELECT 'health_profiles', COUNT(*) FROM healthprofile
  UNION ALL
  SELECT 'settings', COUNT(*) FROM settings
  UNION ALL
  SELECT 'products', COUNT(*) FROM product
  UNION ALL
  SELECT 'protocols', COUNT(*) FROM protocol
  UNION ALL
  SELECT 'user_protocols', COUNT(*) FROM userprotocol
  UNION ALL
  SELECT 'protocol_checkins', COUNT(*) FROM protocolcheckin;
"
Expected: checkins=8, medications>=4, food_logs>=4, symptoms>=3, profiles>=1, health_profiles>=1, settings>=1, products=4, protocols=4, user_protocols=2, protocol_checkins=28.

2. Seeding Idempotency

# Run seed twice — second run must show all skipped, zero errors
make run seed
make run seed
Expected: Second run shows 0 loaded, N skipped for every loader. No errors.

3. Landing Page (/)

  1. Open http://localhost:3000
  2. Verify the landing page loads without console errors
  3. Verify no references to “Alex Rivera” appear anywhere

4. Individual User Dashboard (/shrine-checkins)

Note: Clerk auth is required for protected routes. In dev mode with mock auth, you may need to configure a test user in Clerk’s dev dashboard matching sarah@test.syntropyhealth.bio.
  1. Navigate to the dashboard
  2. Verify 8 check-ins display in the list:
    • 3 text, 2 voice, 1 chat, 1 phone, 1 pending
    • Sorted by recency (most recent first)
    • Each shows correct type icon, summary, and status badge
  3. Verify check-in detail view:
    • Click any processed check-in
    • Raw content / transcript is visible
    • Health topics tags render correctly
    • Sentiment badge shows (positive/neutral/negative)
  4. Verify no “Alex Rivera” appears in any UI element

5. Health Data Views

Medications

  1. Navigate to the medications view/tab
  2. Verify 4 medication entries visible:
    • Metformin 500mg (twice daily)
    • Vitamin D3 5000 IU (daily)
    • Magnesium Glycinate 400mg (nightly)
    • Ibuprofen 400mg (as needed)

Food Logs

  1. Navigate to the food tracking view/tab
  2. Verify 4 food entries with calorie data:
    • Oatmeal with blueberries (350 cal)
    • Green tea (2 cal)
    • Grilled chicken salad (520 cal)
    • Green protein smoothie (310 cal)

Symptoms

  1. Navigate to the symptoms view/tab
  2. Verify 3 symptom entries:
    • Knee discomfort (mild, right knee)
    • Insomnia (moderate)
    • Headache + Fatigue (moderate)

6. Profile Page (/profile)

  1. Verify display name shows “Sarah Chen”
  2. Verify bio: “Health-conscious entrepreneur exploring integrative wellness.”
  3. Verify location: “San Francisco, CA”
  4. Health profile shows:
    • Conditions: Type 2 Diabetes, Vitamin D Deficiency
    • Supplements: Vitamin D3, Magnesium, Omega-3, Probiotic
    • Goals: sleep quality, blood sugar, vitamin D, exercise
    • Allergies: Shellfish
    • Diet: Mediterranean

7. Settings Page (/settings)

  1. Verify subscription plan shows “Free”
  2. Verify herbal supplement toggle is ON
  3. Verify notification frequency = 2

8. Catalog (/catalog)

  1. Verify 4 products render in the catalog grid
  2. Verify 4 protocols are visible
  3. Verify filtering by category works (supplements, nutrition, fitness, wellness)
  4. Verify protocol detail view shows instructions, duration, linked product

9. Protocol Pipeline

Catalog Protocols

  1. Navigate to /catalog
  2. Verify 4 protocols display with partner name “Sarah Chen” (or Chen Wellness Co)
  3. Each protocol shows: name, description, duration, usage type, linked product

User Protocol Adoption

  1. Log in as individual user (Sarah Chen)
  2. Navigate to the Lounge / adopted protocols view
  3. Verify 2 adopted protocols: Vitamin D3 Supplementation, Magnesium for Sleep
  4. Each shows: status “active”, started ~14 days ago, share data enabled

Adherence / Streaks

  1. For each adopted protocol, verify adherence heatmap:
    • 14 days of history
    • Days 1-7 ago: all “taken” (green) — 7-day current streak
    • Day 0 (today): “pending”
    • Days 8-13 ago: mix of taken/missed/skipped
  2. Verify PDC (Proportion of Days Covered) calculation:
    • 10 taken / 14 days = ~71% (below 80% adherence threshold)

Partner Portal Protocols

  1. Log in as partner user (sarah+shopify)
  2. Navigate to /partner/portal
  3. Verify 4 protocols listed in management view
  4. Verify protocol detail shows: adopter count, adherence stats

Database Verification

# Protocol pipeline counts
PGPASSWORD=postgres psql -h localhost -U postgres -d syntropy_journals -c "
  SELECT p.name, p.usage, p.duration_days,
    (SELECT COUNT(*) FROM userprotocol up WHERE up.protocol_id = p.id) as adopters,
    (SELECT COUNT(*) FROM protocolcheckin pc
     JOIN userprotocol up ON up.id = pc.user_protocol_id
     WHERE up.protocol_id = p.id) as checkins
  FROM protocol p ORDER BY p.name;
"

# Adherence detail for adopted protocols
PGPASSWORD=postgres psql -h localhost -U postgres -d syntropy_journals -c "
  SELECT up.id, p.name, pc.status, COUNT(*) as count
  FROM protocolcheckin pc
  JOIN userprotocol up ON up.id = pc.user_protocol_id
  JOIN protocol p ON p.id = up.protocol_id
  GROUP BY up.id, p.name, pc.status
  ORDER BY up.id, pc.status;
"

10. ShrineAI Chat (/longevitian/shrine-chat)

  1. Open the chat interface
  2. Type a message and submit
  3. In make run dev-mock mode: verify mock response renders
  4. In make run dev mode: verify LLM response streams correctly
  5. Verify no stale “Alex” references in system prompts or greeting

QA Checklist: Regression

Run after any significant change to verify nothing is broken.

Automated Checks

# Unit tests (fast, no DB required)
make test-unit

# Full test suite
make test

# Lint
make lint
Expected: Unit tests 1060+ passing, 18 known failures (pre-existing adherence + branding tests).

Manual Smoke Test

StepRouteWhat to Check
1/Landing page loads, no console errors
2/sign-inClerk sign-in form renders
3/shrine-checkinsDashboard loads with check-ins (requires auth)
4/longevitian/shrine-chatChat interface loads, input works
5/catalogProduct grid renders with images
6/profileProfile data displays correctly
7/settingsSettings form is interactive
8/partner/portalPartner dashboard loads (requires partner auth)

QA Checklist: Seeding Pipeline

For verifying changes to scripts/seed/ or data/seed/.

Unit Tests

# Seed data contract tests (38 tests)
APP_ENV=test uv run python -m pytest tests/unit/test_seed_data.py -v
Expected: All 38 pass. These verify:
  • Sarah Chen as individual user (email, name, type)
  • Partner user exists with org name
  • All seed data is internally consistent (checkin linkages, no Alex Rivera references)
  • Protocol pipeline: adoptions reference valid protocols, 7-day streak, valid statuses
  • Subscription plans: 3 tiers, Free first, Pro is popular
  • UserType validity: all seed users use valid enum values (ADMIN/USER/GUEST)
  • SeedResult dataclass works correctly

Integration Test

# Seed against a fresh DB
make db-reset && make db-migrate && make run seed
Expected: All loaders show loaded > 0, errors = 0.

Edge Cases

ScenarioHow to TestExpected
Empty DBmake db-reset && make db-migrate && make run seedAll records inserted
Re-seedRun make run seed twiceSecond run: all skipped
Missing DBStop Docker, run seedClear connection error (not a crash)

QA Checklist: Store Referral Attribution

Verify the full referral pipeline: link click → cookie capture → signup → DB record → portal display.

Prerequisites

# Seed data must include store (run after db-migrate)
make run seed
# Verify store exists
PGPASSWORD=postgres psql -h localhost -U postgres -d syntropy_journals \
  -c "SELECT name, slug, referral_code, status FROM store;"
Expected: One row — “Chen Wellness Co”, slug “chen-wellness-co”, code “store-chen-well-a1b2”, status “active”.
uv run python -m scripts.qa.referral_link
Prints the referral URL and step-by-step instructions.
  1. Open http://localhost:3000/?ref=store-chen-well-a1b2
  2. Open DevTools → Application → Cookies → localhost
  3. Verify cookie ref exists with value store-chen-well-a1b2 and max-age ~30 days
  4. Verify browser console: document.cookie.match(/ref=([^;]*)/) returns the code
  1. From the landing page (with cookie set), click Sign Up / Get Started
  2. Complete Clerk sign-up with a new test email (not the seeded demo users)
  3. After sign-up completes, the auth callback fires handle_referral_cookie

3. Database Verification

# Check StoreReferral record was created
PGPASSWORD=postgres psql -h localhost -U postgres -d syntropy_journals -c "
  SELECT id, store_id, referred_clerk_user_id, referral_code, status,
         clicked_at, signed_up_at
  FROM store_referral
  WHERE referral_code = 'store-chen-well-a1b2'
  ORDER BY id DESC;
"
Expected: A row with status = 'signed_up', referred_clerk_user_id matching the new user’s Clerk ID, and signed_up_at populated.
# Check aggregate stats
PGPASSWORD=postgres psql -h localhost -U postgres -d syntropy_journals -c "
  SELECT
    COUNT(*) as total_clicks,
    COUNT(*) FILTER (WHERE status IN ('signed_up', 'active')) as total_signups,
    COUNT(*) FILTER (WHERE status IN ('signed_up', 'active')
                     AND signed_up_at >= NOW() - INTERVAL '30 days') as recent_30d
  FROM store_referral
  WHERE store_id = (SELECT id FROM store WHERE slug = 'chen-wellness-co');
"
Expected: total_clicks >= 4 (3 seeded + 1 new), total_signups >= 2 (1 seeded + 1 new).

4. Partner Portal Verification

  1. Log in as the partner user (partner@syntropyhealth.com / Clerk dev)
  2. Navigate to /partners/dashboard
  3. Verify the “Referrals” tab shows:
    • Referral link containing store-chen-well-a1b2
    • Total signups count matches DB
    • Recent signups (30d) count matches DB
  4. Copy referral link button works (check clipboard)

5. Edge Cases

ScenarioHow to TestExpected
Invalid referral codeVisit /?ref=!!!invalid!!!Cookie not set (sanitized to empty)
Non-store referralVisit /?ref=friend-codeCookie set, but no StoreReferral row — only User.referral_source
Expired cookieClear cookies, sign up without ?ref=No referral attribution at all
Duplicate signupSign up same user twice with same refOnly one StoreReferral row per user
Partner without StoreLog in as partner before seeding storePortal falls back to User.referral_code

6. PostHog Verification (if configured)

If POSTHOG_PROJECT_API_KEY is set, check PostHog dashboard for:
  • referral_link_clicked event with ref: store-chen-well-a1b2
  • referral_signup_completed event with referral_source: store-chen-well-a1b2
  • store_referral_signup event with referral_code: store-chen-well-a1b2

Protocol Extraction — User Journey

The protocol extraction pipeline creates Protocol records from external data sources. With PRD-17’s integration architecture, protocol sources are divided into two categories:

SDK-Integrated Sources (API-driven)

Partners using e-commerce platforms push protocols via the Integration API:
Shopify App → POST /api/v1/protocols → Protocol(source="shopify")
Amazon App  → POST /api/v1/protocols → Protocol(source="amazon")
WooCommerce → POST /api/v1/protocols → Protocol(source="woocommerce")
These partners manage protocol CRUD in their own platform (Shopify admin, etc.). The Integration API (PRD-17 Phase 6, pending) handles inbound sync. No extraction logic runs for SDK sources — they push structured data directly.

Non-SDK Sources (Extraction-driven)

For protocols that don’t come through an SDK integration, the protocol extractor parses unstructured data:
SourceStatusHow It Works
Shopify orders (consumer)Implementedprotocol_extractor.py — regex-based dosage parsing from order line item titles
Email forwardingFutureUser forwards practitioner email → LLM extracts protocol details
Manual entryImplementedProtocol form (partner portal on hold; will move to Shopify app) → create_protocol_sync(source="manual")
ShrineAI chatFutureAgent tool creates protocol from conversation context

Consumer Shopify Extraction Flow (Current)

Consumer connects Shopify store
  → POST /api/consumer/shopify/sync
    → Fetch orders via Shopify GraphQL
    → extract_supplement_products() — keyword heuristic filter
    → extract_dosage_from_title() — regex: "5000 IU" → {amount: "5000 IU", frequency: "daily"}
    → create_protocol_from_line_item() → Protocol dict
    → save_protocols_from_sync() → Protocol + auto-adopt UserProtocol
Key files:
  • functions/consumer/protocol_extractor.py — dosage regex, protocol builder
  • functions/consumer/shopify_orders.py — supplement keyword filter
  • functions/consumer/db_utils.py:save_protocols_from_sync — DB persistence with dedup

Extraction vs SDK Decision Matrix

SignalUse ExtractionUse SDK API
Partner has Shopify/Amazon/WooCommerce appNoYes
Consumer connected personal Shopify storeYesNo
User forwards an email with protocol infoYes (future)No
Partner creates protocol manuallyNo (form-based, no extraction)No
Design principle: SDK partners push structured ProtocolCreateRequest payloads. Extraction only runs for unstructured sources where the system must infer protocol details from raw text, product titles, or email content.

Verifying Data via psql

Quick-reference queries for inspecting seeded data:
# Open a psql shell
make docker-db-shell

# Or manually:
PGPASSWORD=postgres psql -h localhost -U postgres -d syntropy_journals
-- All demo users
SELECT id, email, user_type, is_active FROM "user"
WHERE email LIKE '%syntropyhealth%';

-- Check-ins with type and status
SELECT checkin_id, checkin_type, status, sentiment,
       LEFT(summary, 50) as summary_preview
FROM checkins WHERE checkin_id LIKE 'CHK-seed%'
ORDER BY created_at DESC;

-- Health entries per check-in
SELECT c.checkin_id,
  (SELECT COUNT(*) FROM medication_entries m WHERE m.checkin_id = c.checkin_id) as meds,
  (SELECT COUNT(*) FROM food_log_entries f WHERE f.checkin_id = c.checkin_id) as foods,
  (SELECT COUNT(*) FROM symptom_entries s WHERE s.checkin_id = c.checkin_id) as symptoms
FROM checkins c WHERE c.checkin_id LIKE 'CHK-seed%';

-- Profile data
SELECT u.email, p.display_name, p.bio
FROM "user" u JOIN userprofile p ON p.user_id = u.id
WHERE u.email LIKE '%syntropyhealth%';

Troubleshooting

ProblemCauseFix
make run seed shows connection errorPostgreSQL not runningmake docker-db
Seed shows 0 loaded, 0 skippedTables don’t existmake db-migrate first
Duplicate key errorStale data from previous schemamake db-reset && make db-migrate && make run seed
App shows blank dashboardNo seeded data for current userVerify Clerk user ID matches user_demo_sarah_chen
”Alex Rivera” still appearsBrowser cache or stale stateHard refresh (Ctrl+Shift+R), clear local storage