384 lines
8.1 KiB
Markdown
384 lines
8.1 KiB
Markdown
# Sojorn Seeding Setup Guide
|
|
|
|
## Prerequisites
|
|
|
|
- Supabase project deployed
|
|
- Categories seeded (run `seed_categories.sql`)
|
|
- At least one real user account created (for testing)
|
|
|
|
## Setup Order
|
|
|
|
Run these scripts in order in your Supabase SQL Editor:
|
|
https://app.supabase.com/project/zwkihedetedlatyvplyz/sql/new
|
|
|
|
### Step 1: Add is_official Column
|
|
|
|
**File:** `supabase/migrations/add_is_official_column.sql`
|
|
|
|
**Purpose:** Adds `is_official` boolean column to profiles table
|
|
|
|
**What it does:**
|
|
- Adds `is_official BOOLEAN DEFAULT false` to profiles
|
|
- Creates index for performance
|
|
- Adds documentation comment
|
|
|
|
**Run this:**
|
|
```sql
|
|
-- Paste contents of add_is_official_column.sql
|
|
```
|
|
|
|
**Verify:**
|
|
```sql
|
|
SELECT column_name, data_type, column_default
|
|
FROM information_schema.columns
|
|
WHERE table_name = 'profiles' AND column_name = 'is_official';
|
|
|
|
-- Should return:
|
|
-- column_name | data_type | column_default
|
|
-- is_official | boolean | false
|
|
```
|
|
|
|
---
|
|
|
|
### Step 2: Create Official Accounts
|
|
|
|
**File:** `supabase/seed/seed_official_accounts.sql`
|
|
|
|
**Purpose:** Creates 3 official Sojorn accounts
|
|
|
|
**What it does:**
|
|
- Creates @sojorn (platform announcements)
|
|
- Creates @sojorn_read (reading content)
|
|
- Creates @sojorn_write (writing prompts)
|
|
- Sets disabled passwords (cannot log in)
|
|
- Marks `is_official = true`
|
|
- Creates trust_state records
|
|
- Adds RLS policies
|
|
|
|
**Run this:**
|
|
```sql
|
|
-- Paste contents of seed_official_accounts.sql
|
|
```
|
|
|
|
**Verify:**
|
|
```sql
|
|
SELECT handle, display_name, is_official, bio
|
|
FROM profiles
|
|
WHERE is_official = true;
|
|
|
|
-- Should return 3 rows:
|
|
-- sojorn | Sojorn | true | Official Sojorn account • Platform updates...
|
|
-- sojorn_read | Sojorn Reading | true | Excerpts, quotes, and reading prompts...
|
|
-- sojorn_write | Sojorn Writing | true | Writing prompts and gentle reflections
|
|
```
|
|
|
|
**Expected output:**
|
|
```
|
|
NOTICE: Official accounts created successfully
|
|
NOTICE: @sojorn: [UUID]
|
|
NOTICE: @sojorn_read: [UUID]
|
|
NOTICE: @sojorn_write: [UUID]
|
|
```
|
|
|
|
---
|
|
|
|
### Step 3: Seed Content
|
|
|
|
**File:** `supabase/seed/seed_content.sql`
|
|
|
|
**Purpose:** Creates ~55 posts from official accounts
|
|
|
|
**What it does:**
|
|
- Inserts platform transparency posts
|
|
- Inserts public domain poetry
|
|
- Inserts reading reflections
|
|
- Inserts writing prompts
|
|
- Inserts observational content
|
|
- Backdates posts over 14 days
|
|
- Sets all engagement metrics to 0
|
|
|
|
**Run this:**
|
|
```sql
|
|
-- Paste contents of seed_content.sql
|
|
```
|
|
|
|
**Verify:**
|
|
```sql
|
|
SELECT
|
|
p.handle,
|
|
COUNT(posts.*) as post_count
|
|
FROM posts
|
|
JOIN profiles p ON p.id = posts.author_id
|
|
WHERE p.is_official = true
|
|
GROUP BY p.handle
|
|
ORDER BY p.handle;
|
|
|
|
-- Should return approximately:
|
|
-- sojorn | 5
|
|
-- sojorn_read | 20-25
|
|
-- sojorn_write | 25-30
|
|
```
|
|
|
|
**Check timestamp distribution:**
|
|
```sql
|
|
SELECT
|
|
DATE(created_at) as post_date,
|
|
COUNT(*) as posts
|
|
FROM posts
|
|
JOIN profiles p ON p.id = posts.author_id
|
|
WHERE p.is_official = true
|
|
GROUP BY DATE(created_at)
|
|
ORDER BY post_date;
|
|
|
|
-- Should show posts spread over ~14 days
|
|
```
|
|
|
|
**Check engagement (should all be 0):**
|
|
```sql
|
|
SELECT
|
|
like_count,
|
|
save_count,
|
|
comment_count,
|
|
view_count,
|
|
COUNT(*) as posts_with_these_values
|
|
FROM post_metrics pm
|
|
JOIN posts ON posts.id = pm.post_id
|
|
JOIN profiles p ON p.id = posts.author_id
|
|
WHERE p.is_official = true
|
|
GROUP BY like_count, save_count, comment_count, view_count;
|
|
|
|
-- Should return:
|
|
-- 0 | 0 | 0 | 0 | [total_count]
|
|
```
|
|
|
|
**Expected output:**
|
|
```
|
|
NOTICE: Seed content created successfully
|
|
NOTICE: Posts span 14 days, backdated from NOW
|
|
NOTICE: All engagement metrics set to 0 (no fake activity)
|
|
```
|
|
|
|
---
|
|
|
|
## Testing in Flutter App
|
|
|
|
### 1. Update Flutter Dependencies
|
|
|
|
Make sure you've pulled the latest code with the updated Profile model:
|
|
|
|
```dart
|
|
// lib/models/profile.dart should include:
|
|
final bool isOfficial;
|
|
```
|
|
|
|
### 2. Run the App
|
|
|
|
```bash
|
|
cd sojorn_app
|
|
flutter run -d chrome
|
|
```
|
|
|
|
### 3. Verify Official Badge
|
|
|
|
- Navigate to the Sojorn feed
|
|
- Look for posts from official accounts
|
|
- Should see **[SOJORN]** badge next to author name
|
|
- Badge should be soft blue (AppTheme.info)
|
|
- Badge should be small (8px font)
|
|
|
|
### 4. Verify No Fake Engagement
|
|
|
|
- Official posts should show 0 likes, 0 saves, 0 comments
|
|
- No "trending" or "recommended" language
|
|
- Just the content and the [SOJORN] badge
|
|
|
|
---
|
|
|
|
## Troubleshooting
|
|
|
|
### Error: "column is_official does not exist"
|
|
|
|
**Cause:** Step 1 (migration) was skipped
|
|
|
|
**Fix:**
|
|
```sql
|
|
-- Run add_is_official_column.sql first
|
|
ALTER TABLE profiles
|
|
ADD COLUMN IF NOT EXISTS is_official BOOLEAN DEFAULT false;
|
|
```
|
|
|
|
### Error: "No users found" in seed_content.sql
|
|
|
|
**Cause:** Official accounts not created yet
|
|
|
|
**Fix:** Run Step 2 (seed_official_accounts.sql) first
|
|
|
|
### Error: "duplicate key value violates unique constraint"
|
|
|
|
**Cause:** Official accounts already exist
|
|
|
|
**Fix:** Either:
|
|
1. Delete and recreate:
|
|
```sql
|
|
DELETE FROM profiles WHERE is_official = true;
|
|
-- Then re-run seed_official_accounts.sql
|
|
```
|
|
|
|
2. Or skip seed_official_accounts.sql if already done
|
|
|
|
### Official badge not showing in Flutter
|
|
|
|
**Cause:** Profile model not updated or API not returning is_official
|
|
|
|
**Fix:**
|
|
1. Check Profile model includes `isOfficial` field
|
|
2. Check API query includes `is_official` in SELECT:
|
|
```typescript
|
|
.select('*, author:profiles(*)')
|
|
// Make sure profiles(*) includes is_official
|
|
```
|
|
|
|
---
|
|
|
|
## Post-Seeding Checks
|
|
|
|
### Feed Distribution
|
|
|
|
Check how much of your feed is official content:
|
|
|
|
```sql
|
|
WITH feed_stats AS (
|
|
SELECT
|
|
p.is_official,
|
|
COUNT(*) as count
|
|
FROM posts
|
|
JOIN profiles p ON p.id = posts.author_id
|
|
WHERE posts.status = 'active'
|
|
GROUP BY p.is_official
|
|
)
|
|
SELECT
|
|
CASE
|
|
WHEN is_official THEN 'Official'
|
|
ELSE 'User'
|
|
END as account_type,
|
|
count,
|
|
ROUND(100.0 * count / SUM(count) OVER (), 1) as percentage
|
|
FROM feed_stats;
|
|
|
|
-- Expected (day 1):
|
|
-- Official | 55 | 100.0%
|
|
-- User | 0 | 0.0%
|
|
|
|
-- Expected (after users join):
|
|
-- Official | 55 | ~50-80%
|
|
-- User | XX | ~20-50%
|
|
```
|
|
|
|
### Content by Category
|
|
|
|
```sql
|
|
SELECT
|
|
c.name as category,
|
|
COUNT(*) as posts
|
|
FROM posts
|
|
JOIN profiles p ON p.id = posts.author_id
|
|
JOIN categories c ON c.id = posts.category_id
|
|
WHERE p.is_official = true
|
|
GROUP BY c.name
|
|
ORDER BY posts DESC;
|
|
|
|
-- Should show balanced distribution across categories
|
|
```
|
|
|
|
---
|
|
|
|
## Monthly Maintenance
|
|
|
|
### Check Official Content Ratio
|
|
|
|
Run monthly to ensure official content isn't dominating:
|
|
|
|
```sql
|
|
SELECT
|
|
DATE_TRUNC('week', created_at) as week,
|
|
SUM(CASE WHEN p.is_official THEN 1 ELSE 0 END) as official_posts,
|
|
SUM(CASE WHEN NOT p.is_official THEN 1 ELSE 0 END) as user_posts,
|
|
ROUND(
|
|
100.0 * SUM(CASE WHEN p.is_official THEN 1 ELSE 0 END) / COUNT(*),
|
|
1
|
|
) as pct_official
|
|
FROM posts
|
|
JOIN profiles p ON p.id = posts.author_id
|
|
WHERE posts.status = 'active'
|
|
AND created_at >= NOW() - INTERVAL '4 weeks'
|
|
GROUP BY week
|
|
ORDER BY week DESC;
|
|
```
|
|
|
|
**Target ratios:**
|
|
- Week 1-2: 80-100% official (okay, platform is new)
|
|
- Week 3-4: 50-80% official (user content growing)
|
|
- Month 2+: 10-30% official (user content dominant)
|
|
- Month 6+: 0-10% official (archive old posts)
|
|
|
|
### Archive Old Official Posts (After 6 Months)
|
|
|
|
```sql
|
|
-- Optional: Move old official posts to archived status
|
|
UPDATE posts
|
|
SET status = 'archived'
|
|
WHERE author_id IN (
|
|
SELECT id FROM profiles WHERE is_official = true
|
|
)
|
|
AND created_at < NOW() - INTERVAL '6 months'
|
|
AND status = 'active';
|
|
```
|
|
|
|
---
|
|
|
|
## Rollback (If Needed)
|
|
|
|
### Remove All Seed Content
|
|
|
|
```sql
|
|
-- Delete seed posts
|
|
DELETE FROM posts
|
|
WHERE author_id IN (
|
|
SELECT id FROM profiles WHERE is_official = true
|
|
);
|
|
|
|
-- Delete official accounts
|
|
DELETE FROM profiles WHERE is_official = true;
|
|
|
|
-- Remove column (optional)
|
|
ALTER TABLE profiles DROP COLUMN IF EXISTS is_official;
|
|
```
|
|
|
|
---
|
|
|
|
## Summary
|
|
|
|
**What you should have after seeding:**
|
|
|
|
✅ 3 official accounts (@sojorn, @sojorn_read, @sojorn_write)
|
|
✅ ~55 posts backdated over 14 days
|
|
✅ 0 fake engagement on all posts
|
|
✅ Clear [SOJORN] badge in UI
|
|
✅ Balanced content across categories
|
|
✅ New users see content immediately
|
|
✅ Trust preserved through transparency
|
|
|
|
**What you should NOT have:**
|
|
|
|
❌ Fake user personas
|
|
❌ Inflated metrics
|
|
❌ Hidden origin
|
|
❌ Synthetic conversations
|
|
❌ Deceptive language
|
|
|
|
---
|
|
|
|
**Philosophy:** Honest hospitality, not deception.
|
|
|
|
**Next:** Monitor feed ratio monthly and plan archival after 6 months.
|