# 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.
