--- allowed-tools: Read, mcp__sauci-prod__execute_sql, mcp__sauci-prod__list_tables argument-hint: [--all] [--explicit] [--apply] [--limit=100] description: Review and automatically fix issues with questions (text, targeting, intensity, props, duplicates) --- # Question Fix Agent Fix questions in category: $ARGUMENTS ## Task Overview Analyze questions across all packs in a category and apply fixes for issues across 6 dimensions: 1. **Text Phrasing** - Improve wording, remove wishy-washy language 2. **Audience Targeting** - Fix couple/initiator gender targeting 3. **Intensity Levels** - Correct misassigned intensity ratings 4. **Required Props** - Identify missing physical props/accessories 5. **Inverse Linking** - Detect and link inverse question pairs using inverse_of column 6. **Deletions** - Remove true duplicates, unsafe, or broken questions ## Step 1: Parse Arguments Extract from $ARGUMENTS: - **Category identifier**: Category name (partial match) OR category ID (UUID) - **--all**: Flag indicating the agent should work through all categories sequentially - **--explicit**: Flag indicating this is an explicit/adult category (auto-detected from category.is_explicit if not specified) - **--apply**: If set, apply fixes automatically after review. If not set, just show suggestions. - **--limit**: Max questions to analyze per category (default: 100) ## Step 2: Fetch Data Query the database to get the category and its questions: ```sql -- Get category info SELECT id, name FROM categories WHERE id::text = '' OR name ILIKE '%%' LIMIT 1; ``` ```sql -- Get all packs in the category SELECT p.id, p.name, p.is_explicit FROM question_packs p WHERE p.category_id = '' ORDER BY p.name; ``` ```sql -- Get questions for all packs in the category (include inverse_of for pair detection) SELECT q.id, q.text, q.partner_text, q.intensity, q.allowed_couple_genders, q.target_user_genders, q.required_props, q.inverse_of, p.name as pack_name FROM questions q JOIN question_packs p ON q.pack_id = p.id WHERE p.category_id = '' AND q.deleted_at IS NULL ORDER BY p.name, q.created_at LIMIT ; ``` ```sql -- Get existing props catalog for consistency SELECT DISTINCT unnest(required_props) as prop FROM questions WHERE required_props IS NOT NULL ORDER BY prop; ``` If **--all** flag is set, first list all categories: ```sql SELECT id, name, is_explicit FROM categories ORDER BY name; ``` Then process each category sequentially. ## Step 3: Analyze Each Question For each question, evaluate against these criteria: ### 3.1 Text Analysis **Language Rules:** - ALWAYS use "your partner" - NEVER use "me", "I", "you" (as receiver), "him", "her", or gendered pronouns - Cards are PROPOSALS, NOT interview questions - "Give your partner a massage" not "Would you like to give a massage?" - Avoid wishy-washy language - NO "Would you...", "Have you ever...", "Do you think...", "Maybe we could..." - No time-specific words - NO "tonight", "now", "today", "right now" - Keep concise - ideal length is 5-12 words **Tone Rules (based on is_explicit):** For EXPLICIT packs: - Use tasteful phrasing for common acts: "Have sex in X" (not "fuck in X"), "Perform oral" or "go down on" (not crude oral terms) - BUT keep crude/specific terms when they ARE the activity: "Cum on your partner's tits" (cum is the act), "Use a cock ring" (that's the name) - NEVER sanitize "cum" to "come" - they have different meanings For NON-EXPLICIT packs: - CRITICAL: No sexual acts, crude language, or NSFW content - Keep language romantic, sensual, or playful without graphic terms - Flag explicit content for removal **Partner Text Rules:** - NULL for symmetric activities (both partners do the same thing together) - FILLED for asymmetric activities (one does something to/for the other) - text = what the INITIATOR does - partner_text = what the RECEIVER experiences - Make partner_text APPEALING - frame receiver's experience positively - When initiator causes a response (moan, cum, beg), frame as ALLOWING: - text: "Make your partner moan" -> partner_text: "Let your partner make you moan" - NOT: "Moan for your partner" (sounds forced) ### 3.2 Intensity Analysis Check if current intensity matches the activity: | Level | Name | Description | Examples | |-------|------|-------------|----------| | 1 | Gentle | Emotional bonding, non-sexual | cooking, cuddling, foot massage | | 2 | Warm | Romantic, affectionate touch | slow dance, sensual massage, kissing | | 3 | Playful | Light sexual exploration | oral, mutual masturbation, light roleplay | | 4 | Steamy | Explicit sex, moderate adventure | intercourse, light bondage, anal play | | 5 | Intense | Advanced/BDSM/extreme | impact play, power dynamics, taboo kinks | ### 3.3 Targeting Analysis **Couple Targeting (allowed_couple_genders):** - DEFAULT: null (ALL couples) unless explicit anatomical requirement - Restrict to `['male+male', 'female+male']` ONLY if activity requires penis - Restrict to `['female+male', 'female+female']` ONLY if activity requires vagina - Sex toys are GENDER-NEUTRAL (vibrators, dildos, plugs work for anyone) **Initiator Targeting (target_user_genders):** - DEFAULT: null (anyone can initiate) - Only for asymmetric questions (those with partner_text) - Set based on who does the action in "text" field: - "Swallow your partner's cum" -> partner has penis -> initiator: ['female'] in M+F - "Deep throat your partner" -> partner has penis -> ['female'] in M+F - "Give your partner a massage" -> gender-neutral -> null ### 3.4 Props Analysis **Rules:** - Props are physical items/accessories, not body parts or acts - Use lowercase, short, singular names (e.g., "blindfold", "remote vibrator") - Try to avoid generics such as "toy" (unless the activity can be done with any toy) - Reuse existing prop names from the catalog when possible - If no props are required, set to null ### 3.5 Inverse Linking Analysis **Purpose:** Detect inverse question pairs and link them using the `inverse_of` database column. **Detection Algorithm:** 1. For each asymmetric question (has partner_text), look for its inverse: - Inverse exists if another question has: `text` = this question's `partner_text` AND `partner_text` = this question's `text` 2. Check if the `inverse_of` column is correctly set: - One question in the pair should be the "primary" (inverse_of = NULL) - The other should be the "inverse" (inverse_of = primary's UUID) **Fixing Missing Links:** - If inverse pair exists but neither has `inverse_of` set: - Set the first one (by created_at) as primary (inverse_of = NULL) - Set the second one's inverse_of = primary's UUID - If inverse pair exists but both have `inverse_of = NULL`: - Choose one as primary, link the other - If a question has `inverse_of` pointing to a non-existent question: - Find the actual inverse and fix the link, OR set to NULL if no inverse exists **Important:** - Only asymmetric questions (with partner_text) should have inverse pairs - Symmetric questions (partner_text = NULL) should have inverse_of = NULL - This fixes the database structure, NOT the question content ### 3.6 Deletion Analysis **Deletion Categories:** - `duplicate`: Same `text` AND same initiator as another question - `redundant`: Minor variation with no added value - `off-tone`: Violates explicit vs non-explicit rules - `unsafe`: Coercion, minors, illegal acts, or consent violations - `too-vague`: Not actionable or not a clear proposal - `broken`: Grammar too broken to repair without inventing content - `off-topic`: Not relevant to a couples intimacy app **CRITICAL: Inverse Questions are NOT Duplicates** Asymmetric questions MUST exist in pairs (original + inverse). These are NOT duplicates: ``` Question A: text: "Spank your partner" partner_text: "Be spanked by your partner" Question B (INVERSE - NOT A DUPLICATE): text: "Be spanked by your partner" partner_text: "Spank your partner" ``` These are intentionally paired so both partners get asked about both roles. When reviewing: - If you see two questions where one's `text` matches the other's `partner_text`, they are a VALID INVERSE PAIR - Do NOT flag inverse pairs as duplicates or redundant - Only flag as duplicate if TWO questions have the SAME `text` (or nearly identical `text`) with the SAME initiator role **Rules:** - Prefer edits over deletions when possible - For true duplicates: delete the weaker/less specific version - Do NOT delete for minor wording issues ## Step 4: Output Results Present findings grouped by fix type: ### Summary | Category | Issues Found | Will Fix | |----------|--------------|----------| | Text fixes | X | X | | Intensity fixes | X | X | | Targeting fixes | X | X | | Props fixes | X | X | | Inverse linking fixes | X | X | | Deletions | X | X | | No changes needed | X | - | ### Detailed Changes For each question needing changes, show: ``` **Question ID:** **Current:** / **Current inverse_of:** **Issues:** - Text: - Intensity: -> () - Targeting: - Props: -> - Inverse Link: - - DELETE: - **Fix:** - Text: "" - Partner Text: "" or null - Intensity: - Couple Targets: - Initiator Targets: - Required Props: - inverse_of: ``` ## Step 5: Apply Fixes (if --apply flag) If --apply flag is set, execute the SQL updates: ```sql -- Text/Partner Text/Intensity fixes UPDATE questions SET text = '', partner_text = '', intensity = WHERE id = ''; -- Targeting fixes UPDATE questions SET allowed_couple_genders = ARRAY['...'] or NULL, target_user_genders = ARRAY['...'] or NULL WHERE id = ''; -- Props fixes UPDATE questions SET required_props = ARRAY['...'] or NULL WHERE id = ''; -- Inverse linking fixes -- Link inverse question to its primary UPDATE questions SET inverse_of = '' WHERE id = ''; -- Or clear a broken inverse_of link UPDATE questions SET inverse_of = NULL WHERE id = ''; -- Deletions (soft delete) UPDATE questions SET deleted_at = NOW() WHERE id IN ('', '', ...); ``` After applying, report: - X questions updated - X questions deleted - Any errors encountered If --apply is NOT set, output the SQL statements for manual review. ## Example Usage ``` /fix-questions "Kink" # Review all questions in "Kink" category /fix-questions "Romantic Classics" --apply # Review and apply fixes to category /fix-questions --all # Process all categories sequentially /fix-questions --all --apply # Process and fix all categories /fix-questions 123e4567-e89b-12d3-a456-426614174000 --limit=50 ``` ## Guidelines - DO ask for clarification if category name matches multiple categories - DO group similar issues together for batch fixes - DO prioritize safety issues (delete unsafe content first) - DO preserve the core intent of questions when suggesting text fixes - DO verify inverse pairs exist before flagging duplicates - DO detect and link inverse pairs using the inverse_of column - DON'T change questions that are already well-formed - DON'T suggest overly restrictive targeting (prefer inclusive by default) - DON'T create new prop names when existing ones match - DON'T flag inverse pairs as duplicates (one's text = other's partner_text) - DON'T forget to set inverse_of for detected inverse pairs - this ensures accurate unique question counts