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:
- New JSON response fields the AI will return
- Database columns needed in
activitiesandpost_boost_activitiestables - API changes needed to parse and store the new fields
- 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
| Field | v2 | v3 | Action |
|---|---|---|---|
comment | ✅ | ✅ | No change |
strategy_name | ✅ | ❌ REMOVED | Stop storing. Redundant with strategy_used. |
strategy_used | ✅ | ✅ | No change. Now has 12 possible values (was 6). |
voice_profile | ✅ | ✅ | No change |
voice_description | ✅ | ✅ | No change (was not stored in DB — now should be) |
word_count | ✅ | ✅ | No change |
opener_type | ❌ | ✅ NEW | Store in DB |
engagement_type | ❌ | ✅ NEW | Store in DB |
case_style | ❌ | ✅ NEW | Store in DB |
persona_anchor | ❌ | ✅ NEW | Store in DB |
post_reference | ❌ | ✅ NEW | Store in DB |
confidence_score | ❌ | ✅ NEW | Store 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
| Strategy | Purpose | Word Range |
|---|---|---|
question_lead | Lead with a question, then brief context | 20-50 |
contrarian | Direct polite disagreement | 25-60 |
analogy_bridge | Connect to a parallel domain | 25-60 |
forward_look | Predict where this is heading | 20-50 |
quote_react | Quote a specific line from the post, react to it | 20-50 |
data_point | Bring a relevant statistic or external fact | 25-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
| Column | Type | Table(s) | Description |
|---|---|---|---|
strategy_used | varchar(100) | activities ✅, post_boost_activities NEW | Which of 12 comment structures was used |
voice_profile | text | activities ✅, post_boost_activities NEW | Short stable tag for the user’s voice |
voice_description | text | activities NEW, post_boost_activities NEW | How voice was adapted for this comment |
word_count | int2 | activities ✅, post_boost_activities NEW | Word count of the generated comment |
opener_type | varchar(50) | activities NEW, post_boost_activities NEW | How the comment opens: question, statement, quote, observation, anecdote, reaction |
engagement_type | varchar(50) | activities NEW, post_boost_activities NEW | How comment invites engagement: specific_question, bold_statement, reflection, humble_admission, open_question, none |
case_style | varchar(20) | activities NEW, post_boost_activities NEW | Capitalization style: mixed, lowercase, proper |
persona_anchor | text | activities NEW, post_boost_activities NEW | Specific detail from voice brief used to ground the comment |
post_reference | text | activities NEW, post_boost_activities NEW | Specific detail from the post that the comment responds to |
confidence_score | int2 | activities NEW, post_boost_activities NEW | AI’s self-rated confidence (1-10) on how natural the comment sounds |
ai_analysis | jsonb | activities ✅, post_boost_activities NEW | Full 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 column4.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:
- Query
post_boost_activitiesfor allstrategy_usedvalues already stored for thispost_boost_id - Pass those to the user prompt as a new variable
StructuresToAvoid - The AI sees the avoid list and picks a different structure
- Store the generated comment with its
strategy_usedvalue
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 queryEdge 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 aboveMost 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_avoidto 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)
| Step | Action | Table | Risk |
|---|---|---|---|
| 1 | Run ALTER TABLE to add new columns | activities | Low — all NULL by default |
| 2 | Run ALTER TABLE to add new columns | post_boost_activities | Low — all NULL by default |
| 3 | Update AI prompt to v3 system + user prompt | Backend config | Medium — test with sample posts first |
| 4 | Update API response parser to handle 11 fields | Backend code | Medium — backward compatible if defensive |
| 5 | Remove strategy_name handling | Backend code | Low — stop reading the field |
| 6 | Test with 5 sample posts (auto-comment) | — | Verify all 11 fields are populated |
| 7 | Test with 1 post boost (5 commenters) | — | Verify metadata is stored in post_boost_activities |
| 8 | (Future) Add structures_to_avoid to post boost flow | Backend code | Separate 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
| What | Who | When |
|---|---|---|
Add 7 new columns to activities table | Dev team | Tomorrow |
Add 11 new columns to post_boost_activities table | Dev team | Tomorrow |
| Replace v2 prompts with v3 prompts in backend | Dev team | After column migration |
| Update API parser to handle 11 JSON fields | Dev team | Same time as prompt swap |
Remove strategy_name from code | Dev team | Same time |
| Test auto-comment flow with v3 | Dev + Rahul | After deploy |
| Test post boost flow with v3 | Dev + Rahul | After deploy |
Add structures_to_avoid to post boost flow | Dev team | Future PR (high priority) |
| Monitor confidence scores and structure diversity | Rahul | Ongoing after deploy |