GrowReach v3 — Developer Implementation Guide

Date: July 14, 2026
Purpose: Instructions for developers to update API responses and database tables to support v3 prompt output format


Overview

The v3 system prompt now returns a richer JSON response with 11 fields (up from 6 in v2). This guide covers:

  1. New JSON response fields the AI will return
  2. Database columns needed in activities and post_boost_activities tables
  3. API changes needed to parse and store the new fields
  4. Post boost flow changes needed (separate but related)

1. New JSON Response Format

v2 Response (6 fields — current)

{
  "comment": "comment text",
  "strategy_name": "validate_extend",
  "strategy_used": "validate_extend",
  "voice_profile": "senior AI founder, direct and analytical",
  "voice_description": "Conversational tone, referenced Careem scaling experience",
  "word_count": 67
}

v3 Response (11 fields — new)

{
  "comment": "comment text",
  "strategy_used": "validate_extend",
  "voice_profile": "senior AI founder, direct and analytical",
  "voice_description": "Conversational tone, referenced scaling JioHotstar systems",
  "word_count": 52,
  "opener_type": "statement",
  "engagement_type": "specific_question",
  "case_style": "mixed",
  "persona_anchor": "referenced scaling JioHotstar systems",
  "post_reference": "responded to the claim about AI code quality under load",
  "confidence_score": 8
}

What Changed

Fieldv2v3Action
commentNo change
strategy_name❌ REMOVEDStop storing. Redundant with strategy_used.
strategy_usedNo change. Now has 12 possible values (was 6).
voice_profileNo change
voice_descriptionNo change (was not stored in DB — now should be)
word_countNo change
opener_type✅ NEWStore in DB
engagement_type✅ NEWStore in DB
case_style✅ NEWStore in DB
persona_anchor✅ NEWStore in DB
post_reference✅ NEWStore in DB
confidence_score✅ NEWStore in DB

2. New strategy_used Values

The strategy_used field now has 12 possible values (was 6). Update any validation logic, enums, or UI labels.

v2 Values (6)

validate_extend, challenge_nuance, share_experience, ask_explore, direct_observation, casual_react

v3 Values (12)

validate_extend, challenge_nuance, share_experience, ask_explore, direct_observation, casual_react,
question_lead, contrarian, analogy_bridge, forward_look, quote_react, data_point

New Values Breakdown

StrategyPurposeWord Range
question_leadLead with a question, then brief context20-50
contrarianDirect polite disagreement25-60
analogy_bridgeConnect to a parallel domain25-60
forward_lookPredict where this is heading20-50
quote_reactQuote a specific line from the post, react to it20-50
data_pointBring a relevant statistic or external fact25-60

3. Database Changes

3.1 activities Table — Add New Columns

ALTER TABLE public.activities 
  ADD COLUMN IF NOT EXISTS voice_description text NULL,
  ADD COLUMN IF NOT EXISTS opener_type varchar(50) NULL,
  ADD COLUMN IF NOT EXISTS engagement_type varchar(50) NULL,
  ADD COLUMN IF NOT EXISTS case_style varchar(20) NULL,
  ADD COLUMN IF NOT EXISTS persona_anchor text NULL,
  ADD COLUMN IF NOT EXISTS post_reference text NULL,
  ADD COLUMN IF NOT EXISTS confidence_score int2 NULL;

3.2 post_boost_activities Table — Add ALL Metadata Columns

This table currently only stores comment, liked, and commented. It needs all the metadata fields that activities has, plus the new v3 fields.

ALTER TABLE public.post_boost_activities 
  ADD COLUMN IF NOT EXISTS strategy_used varchar(100) NULL,
  ADD COLUMN IF NOT EXISTS voice_profile text NULL,
  ADD COLUMN IF NOT EXISTS voice_description text NULL,
  ADD COLUMN IF NOT EXISTS word_count int2 DEFAULT 0 NULL,
  ADD COLUMN IF NOT EXISTS opener_type varchar(50) NULL,
  ADD COLUMN IF NOT EXISTS engagement_type varchar(50) NULL,
  ADD COLUMN IF NOT EXISTS case_style varchar(20) NULL,
  ADD COLUMN IF NOT EXISTS persona_anchor text NULL,
  ADD COLUMN IF NOT EXISTS post_reference text NULL,
  ADD COLUMN IF NOT EXISTS confidence_score int2 NULL,
  ADD COLUMN IF NOT EXISTS ai_analysis jsonb NULL;

3.3 Column Reference

ColumnTypeTable(s)Description
strategy_usedvarchar(100)activities ✅, post_boost_activities NEWWhich of 12 comment structures was used
voice_profiletextactivities ✅, post_boost_activities NEWShort stable tag for the user’s voice
voice_descriptiontextactivities NEW, post_boost_activities NEWHow voice was adapted for this comment
word_countint2activities ✅, post_boost_activities NEWWord count of the generated comment
opener_typevarchar(50)activities NEW, post_boost_activities NEWHow the comment opens: question, statement, quote, observation, anecdote, reaction
engagement_typevarchar(50)activities NEW, post_boost_activities NEWHow comment invites engagement: specific_question, bold_statement, reflection, humble_admission, open_question, none
case_stylevarchar(20)activities NEW, post_boost_activities NEWCapitalization style: mixed, lowercase, proper
persona_anchortextactivities NEW, post_boost_activities NEWSpecific detail from voice brief used to ground the comment
post_referencetextactivities NEW, post_boost_activities NEWSpecific detail from the post that the comment responds to
confidence_scoreint2activities NEW, post_boost_activities NEWAI’s self-rated confidence (1-10) on how natural the comment sounds
ai_analysisjsonbactivities ✅, post_boost_activities NEWFull AI response JSON (optional, for debugging)

4. API Changes

4.1 Parse the AI Response

The AI will now return 11 JSON fields. The API needs to parse all of them and store them in the database.

# Pseudocode for parsing v3 AI response
ai_response = json.loads(raw_ai_output)
 
comment_data = {
    "comment": ai_response["comment"],
    "strategy_used": ai_response["strategy_used"],
    "voice_profile": ai_response["voice_profile"],
    "voice_description": ai_response["voice_description"],
    "word_count": ai_response["word_count"],
    "opener_type": ai_response["opener_type"],
    "engagement_type": ai_response["engagement_type"],
    "case_style": ai_response["case_style"],
    "persona_anchor": ai_response["persona_anchor"],
    "post_reference": ai_response["post_reference"],
    "confidence_score": ai_response["confidence_score"],
}
 
# Store in activities or post_boost_activities
# Map each field to its DB column

4.2 Remove strategy_name Handling

The v3 response no longer includes strategy_name. Remove any code that reads or stores this field. Use strategy_used instead.

4.3 Handle Missing Fields Gracefully

If the AI doesn’t return a field (e.g., confidence_score missing), store NULL rather than failing. The AI should always return all fields, but be defensive.

comment_data = {
    "comment": ai_response.get("comment", ""),
    "strategy_used": ai_response.get("strategy_used"),
    "voice_profile": ai_response.get("voice_profile"),
    "voice_description": ai_response.get("voice_description"),
    "word_count": ai_response.get("word_count", 0),
    "opener_type": ai_response.get("opener_type"),
    "engagement_type": ai_response.get("engagement_type"),
    "case_style": ai_response.get("case_style"),
    "persona_anchor": ai_response.get("persona_anchor"),
    "post_reference": ai_response.get("post_reference"),
    "confidence_score": ai_response.get("confidence_score"),
}

4.4 Update the System Prompt and User Prompt

Replace the v2 system prompt and user prompt with the v3 versions:

  • System prompt: growreach-prompts-v2/v3-system-prompt.md
  • User prompt: growreach-prompts-v2/v3-user-prompt.md

The user prompt template variables (Go template syntax `{{.FieldName}}) remain the same. No changes to the input variables — only the output format changed.


5. Post Boost Flow — Structure Coordination (High Priority Enhancement)

This is a backend enhancement that works alongside v3 to solve the post boost convergence problem. It is NOT required for the initial v3 prompt deployment but is high priority for post boost comment quality.

The Problem

When 5 users comment on the same post via post boost, each AI call is independent. The AI doesn’t know what structures other commenters used, so all 5 pick the same structure (validate_extend) and produce similar comments. Data from our analysis showed:

  • 100% of post boost comments used validate_extend (same structure)
  • 55% started with “The point about…” (same opener)
  • 82% ended with “How do you…” (same ending)
  • 91% had exactly 3 paragraphs (same shape)
  • All were 100-126 words (same length)

The Solution

Pass a list of structures already used by previous commenters on this post boost to each subsequent AI call. The AI is told to pick a different structure.

How It Works (Step by Step)

The post boost flow processes commenters sequentially (one after another, not in parallel). This is the current behavior. The structures_to_avoid fix takes advantage of this sequential processing.

Step 1: Generate comment for User A
       → No structures used yet → AI picks validate_extend
       → Store strategy_used = "validate_extend" in post_boost_activities

Step 2: Generate comment for User B
       → Query: "SELECT strategy_used FROM post_boost_activities WHERE post_boost_id = ? AND commented = true"
       → Result: ["validate_extend"]
       → Pass to AI: "Do NOT use validate_extend"
       → AI picks ask_explore instead
       → Store strategy_used = "ask_explore"

Step 3: Generate comment for User C
       → Query returns: ["validate_extend", "ask_explore"]
       → Pass to AI: "Do NOT use validate_extend, ask_explore"
       → AI picks quote_react
       → Store strategy_used = "quote_react"

Step 4: Generate comment for User D
       → Query returns: ["validate_extend", "ask_explore", "quote_react"]
       → AI picks contrarian

Step 5: Generate comment for User E
       → Query returns: ["validate_extend", "ask_explore", "quote_react", "contrarian"]
       → AI picks casual_react

Result: 5 comments on the same post, each using a different structure with different openers, lengths, paragraph counts, and endings.

Critical: Sequential Processing

This fix ONLY works if commenters are processed sequentially — one comment is generated and stored before the next one starts. If commenters are processed in parallel, none of them will see what the others chose because none have finished yet.

The current post boost flow is already sequential. Do not change this to parallel processing without removing the structures_to_avoid feature.

Implementation

When generating comment N for a post boost:

  1. Query post_boost_activities for all strategy_used values already stored for this post_boost_id
  2. Pass those to the user prompt as a new variable StructuresToAvoid
  3. The AI sees the avoid list and picks a different structure
  4. Store the generated comment with its strategy_used value

User Prompt Template Addition

Add this section to the user prompt template (only for post boost flow, not for auto-commenting):

== STRUCTURES TO AVOID ==
The following comment structures have already been used by other commenters on this post. Do NOT use these. Pick a different structure.
{{.StructuresToAvoid}}

For auto-commenting (the activities flow), this section should be omitted entirely or passed as empty. Auto-commenting does not need structure coordination because each comment is on a different post.

Backend Logic

# Pseudocode for post boost comment generation (sequential)
 
for user in post_boost_commenters:
    # Query what strategies were already used on this boost
    used_strategies = db.query(
        "SELECT strategy_used FROM post_boost_activities 
         WHERE post_boost_id = ? AND commented = true 
         AND strategy_used IS NOT NULL
         AND deleted_at IS NULL",
        boost.id
    )
    
    # Format the avoid list for the prompt
    if used_strategies:
        avoid_text = "The following comment structures have already been used by other commenters on this post. Do NOT use these. Pick a different structure.\n"
        avoid_text += "\n".join(f"- {s}" for s in used_strategies)
    else:
        avoid_text = "None yet. You are the first commenter on this post."
    
    # Render the user prompt with the avoid list
    user_prompt = render_template(
        "v3-user-prompt.md",
        Persona=user.persona,
        CommentingStrategy=user.commenting_strategy,
        Post=boost.post,
        StructuresToAvoid=avoid_text  # NEW VARIABLE
    )
    
    # Call the AI
    response = call_gemma4(system_prompt, user_prompt)
    
    # Parse and store the response
    # IMPORTANT: strategy_used must be stored before the next commenter is processed
    store_post_boost_activity(
        post_boost_id=boost.id,
        user_id=user.id,
        comment=response.comment,
        strategy_used=response.strategy_used,
        voice_profile=response.voice_profile,
        word_count=response.word_count,
        # ... all other v3 fields
    )
    # The next iteration of this loop will now see this strategy_used in the query

Edge Case: More Than 12 Commenters

With 12 structures and sequential avoidance, commenter #13 would see all 12 structures in the avoid list. Handle this:

if len(used_strategies) >= 12:
    avoid_text = "All 12 comment structures have been used by other commenters on this post. Pick any structure, but make sure your comment sounds different from the others. Use a different opener, different length, and different ending than the others."
else:
    # Normal avoid logic as above

Most post boosts have 5-10 commenters, so this is a rare edge case.

Fallback

If strategy_used is NULL for all previous activities (because the column was just added and old data doesn’t have it), the structures_to_avoid list will be empty. The AI will behave exactly like it does today — no regression. The fix activates automatically once the column exists and has data.

What NOT to Do

  • Do NOT process post boost commenters in parallel. The sequential processing is required for this fix to work.
  • Do NOT add structures_to_avoid to the auto-commenting flow. Auto-commenting comments on different posts, so there is no convergence problem there.
  • Do NOT block the comment generation if the query fails. If the query to get used strategies fails, pass an empty avoid list and let the AI proceed normally.

6. Validation Values

strategy_used — Valid Values (12)

-- Optional: Add a CHECK constraint
ALTER TABLE public.activities 
  ADD CONSTRAINT check_strategy_used 
  CHECK (strategy_used IN (
    'validate_extend', 'challenge_nuance', 'share_experience', 
    'ask_explore', 'direct_observation', 'casual_react',
    'question_lead', 'contrarian', 'analogy_bridge', 
    'forward_look', 'quote_react', 'data_point'
  ));

opener_type — Valid Values (6)

question, statement, quote, observation, anecdote, reaction

engagement_type — Valid Values (6)

specific_question, bold_statement, reflection, humble_admission, open_question, none

case_style — Valid Values (3)

mixed, lowercase, proper

confidence_score — Valid Range

1 to 10 (integer)

7. Migration Steps (Ordered)

StepActionTableRisk
1Run ALTER TABLE to add new columnsactivitiesLow — all NULL by default
2Run ALTER TABLE to add new columnspost_boost_activitiesLow — all NULL by default
3Update AI prompt to v3 system + user promptBackend configMedium — test with sample posts first
4Update API response parser to handle 11 fieldsBackend codeMedium — backward compatible if defensive
5Remove strategy_name handlingBackend codeLow — stop reading the field
6Test with 5 sample posts (auto-comment)Verify all 11 fields are populated
7Test with 1 post boost (5 commenters)Verify metadata is stored in post_boost_activities
8(Future) Add structures_to_avoid to post boost flowBackend codeSeparate PR

8. Monitoring Queries (After Deployment)

Check Structure Diversity in Post Boost

SELECT 
  post_boost_id,
  strategy_used,
  opener_type,
  engagement_type,
  case_style,
  COUNT(*) as count
FROM post_boost_activities
WHERE deleted_at IS NULL
  AND commented = true
  AND strategy_used IS NOT NULL
GROUP BY post_boost_id, strategy_used, opener_type, engagement_type, case_style
ORDER BY post_boost_id, count DESC;

Check Confidence Score Distribution

SELECT 
  CASE 
    WHEN confidence_score >= 8 THEN 'high (8-10)'
    WHEN confidence_score >= 5 THEN 'medium (5-7)'
    WHEN confidence_score >= 1 THEN 'low (1-4)'
    ELSE 'null'
  END as confidence_bucket,
  COUNT(*) as count
FROM activities
WHERE deleted_at IS NULL
  AND created_at > NOW() - INTERVAL '7 days'
GROUP BY confidence_bucket
ORDER BY confidence_bucket;

Check Opener Type Distribution

SELECT 
  opener_type,
  COUNT(*) as count,
  ROUND(COUNT(*) * 100.0 / SUM(COUNT(*)) OVER(), 1) as percentage
FROM activities
WHERE deleted_at IS NULL
  AND opener_type IS NOT NULL
  AND created_at > NOW() - INTERVAL '7 days'
GROUP BY opener_type
ORDER BY count DESC;

Flag Low Confidence Comments for Review

SELECT 
  id,
  comment,
  strategy_used,
  confidence_score,
  voice_description,
  created_at
FROM activities
WHERE deleted_at IS NULL
  AND confidence_score IS NOT NULL
  AND confidence_score <= 4
ORDER BY created_at DESC
LIMIT 50;

9. Summary

WhatWhoWhen
Add 7 new columns to activities tableDev teamTomorrow
Add 11 new columns to post_boost_activities tableDev teamTomorrow
Replace v2 prompts with v3 prompts in backendDev teamAfter column migration
Update API parser to handle 11 JSON fieldsDev teamSame time as prompt swap
Remove strategy_name from codeDev teamSame time
Test auto-comment flow with v3Dev + RahulAfter deploy
Test post boost flow with v3Dev + RahulAfter deploy
Add structures_to_avoid to post boost flowDev teamFuture PR (high priority)
Monitor confidence scores and structure diversityRahulOngoing after deploy