Skip to content

Commit c9da5e3

Browse files
committed
feat: upgrade suggested actions prompt for leverage-first job generation
Rewrite SUGGESTED_ACTIONS_PROMPT to generate 4-6 high-leverage, tiered actions instead of 2-3 generic ones. Add leverage-first criteria (outside-in, revenue-adjacent, unknown-before-use, hard-to-do-manually), demotion rules, output promise requirements, and self-ranking tiers (hero/primary/secondary). Extend SuggestedActionData with tier and outputPromise fields with validation.
1 parent f577877 commit c9da5e3

File tree

2 files changed

+194
-25
lines changed

2 files changed

+194
-25
lines changed

apps/desktop/src/features/dashboard/data/suggested-actions.test.ts

Lines changed: 98 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,8 +182,8 @@ void test("parseSuggestedActions: accepts conversion and growth categories", ()
182182
});
183183

184184
void test("SUGGESTED_ACTIONS_PROMPT lists fixed card titles", () => {
185-
assert.ok(SUGGESTED_ACTIONS_PROMPT.includes("Find launch communities"));
186-
assert.ok(SUGGESTED_ACTIONS_PROMPT.includes("Draft Product Hunt launch"));
185+
assert.ok(SUGGESTED_ACTIONS_PROMPT.includes("Launch on Product Hunt"));
186+
assert.ok(SUGGESTED_ACTIONS_PROMPT.includes("Find SEO quick wins"));
187187
});
188188

189189
// ---------------------------------------------------------------------------
@@ -310,3 +310,99 @@ void test("CATEGORY_TO_SPECIALIST: exports mapping for all 6 categories", () =>
310310
}
311311
});
312312

313+
// ---------------------------------------------------------------------------
314+
// Tier & outputPromise validation
315+
// ---------------------------------------------------------------------------
316+
317+
void test("parseSuggestedActions: accepts actions with valid tier field", () => {
318+
for (const tier of ["hero", "primary", "secondary"]) {
319+
const input = JSON.stringify([
320+
{ id: "tiered", title: "Tiered", promise: "p", description: "d", category: "seo", skills: [], prompt: "pr", tier },
321+
]);
322+
const result = parseSuggestedActions(input);
323+
assert.equal(result.length, 1, `Should accept tier="${tier}"`);
324+
assert.equal((result[0] as Record<string, unknown>).tier, tier);
325+
}
326+
});
327+
328+
void test("parseSuggestedActions: rejects actions with invalid tier value", () => {
329+
const input = JSON.stringify([
330+
{ id: "bad-tier", title: "Bad", promise: "p", description: "d", category: "seo", skills: [], prompt: "pr", tier: "invalid" },
331+
]);
332+
const result = parseSuggestedActions(input);
333+
assert.equal(result.length, 0);
334+
});
335+
336+
void test("parseSuggestedActions: accepts actions without tier field (backward compat)", () => {
337+
const input = JSON.stringify([
338+
{ id: "no-tier", title: "No Tier", promise: "p", description: "d", category: "seo", skills: [], prompt: "pr" },
339+
]);
340+
const result = parseSuggestedActions(input);
341+
assert.equal(result.length, 1);
342+
assert.equal(result[0]!.id, "no-tier");
343+
});
344+
345+
void test("parseSuggestedActions: accepts actions with valid outputPromise field", () => {
346+
const input = JSON.stringify([
347+
{ id: "op", title: "Op", promise: "p", description: "d", category: "seo", skills: [], prompt: "pr", outputPromise: "5 search wedges + page angles" },
348+
]);
349+
const result = parseSuggestedActions(input);
350+
assert.equal(result.length, 1);
351+
assert.equal((result[0] as Record<string, unknown>).outputPromise, "5 search wedges + page angles");
352+
});
353+
354+
void test("parseSuggestedActions: rejects actions with empty outputPromise string", () => {
355+
const input = JSON.stringify([
356+
{ id: "empty-op", title: "Empty", promise: "p", description: "d", category: "seo", skills: [], prompt: "pr", outputPromise: "" },
357+
]);
358+
const result = parseSuggestedActions(input);
359+
assert.equal(result.length, 0);
360+
});
361+
362+
void test("toActionCard: preserves tier and outputPromise when present", () => {
363+
const data = {
364+
id: "full-card",
365+
title: "Full",
366+
promise: "p",
367+
description: "d",
368+
category: "research" as const,
369+
skills: ["seo-audit"],
370+
prompt: "pr",
371+
tier: "hero" as const,
372+
outputPromise: "5 search wedges + page angles",
373+
};
374+
const card = toActionCard(data);
375+
assert.equal((card as Record<string, unknown>).tier, "hero");
376+
assert.equal((card as Record<string, unknown>).outputPromise, "5 search wedges + page angles");
377+
});
378+
379+
// ---------------------------------------------------------------------------
380+
// Updated prompt content assertions
381+
// ---------------------------------------------------------------------------
382+
383+
void test("SUGGESTED_ACTIONS_PROMPT: requests 4-6 actions", () => {
384+
assert.ok(SUGGESTED_ACTIONS_PROMPT.includes("4-6"), "Should request 4-6 actions");
385+
assert.ok(!SUGGESTED_ACTIONS_PROMPT.includes("suggest 2-3"), "Should no longer request 2-3 actions");
386+
});
387+
388+
void test("SUGGESTED_ACTIONS_PROMPT: includes leverage criteria keywords", () => {
389+
const lower = SUGGESTED_ACTIONS_PROMPT.toLowerCase();
390+
assert.ok(lower.includes("outside-in"), "Should include outside-in");
391+
assert.ok(lower.includes("revenue-adjacent"), "Should include revenue-adjacent");
392+
assert.ok(lower.includes("unknown-before-use"), "Should include unknown-before-use");
393+
assert.ok(lower.includes("hard-to-do-manually"), "Should include hard-to-do-manually");
394+
});
395+
396+
void test("SUGGESTED_ACTIONS_PROMPT: includes demotion rules", () => {
397+
assert.ok(SUGGESTED_ACTIONS_PROMPT.toLowerCase().includes("demote") || SUGGESTED_ACTIONS_PROMPT.toLowerCase().includes("avoid"), "Should include demotion guidance");
398+
assert.ok(SUGGESTED_ACTIONS_PROMPT.includes("internal cleanup") || SUGGESTED_ACTIONS_PROMPT.includes("good hygiene"), "Should mention weak job types to avoid");
399+
});
400+
401+
void test("SUGGESTED_ACTIONS_PROMPT: includes tier and outputPromise in JSON schema", () => {
402+
assert.ok(SUGGESTED_ACTIONS_PROMPT.includes('"tier"'), "Should include tier field in schema");
403+
assert.ok(SUGGESTED_ACTIONS_PROMPT.includes('"outputPromise"'), "Should include outputPromise field in schema");
404+
assert.ok(SUGGESTED_ACTIONS_PROMPT.includes('"hero"'), "Should mention hero tier");
405+
assert.ok(SUGGESTED_ACTIONS_PROMPT.includes('"primary"'), "Should mention primary tier");
406+
assert.ok(SUGGESTED_ACTIONS_PROMPT.includes('"secondary"'), "Should mention secondary tier");
407+
});
408+

apps/desktop/src/features/dashboard/data/suggested-actions.ts

Lines changed: 96 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,10 @@ export interface SuggestedActionData {
2929
prompt: string;
3030
/** Short scannable deliverable label, e.g. "3 hero rewrites" */
3131
outputType?: string;
32+
/** Model-assigned tier: hero (1), primary (2-3), or secondary (1-2) */
33+
tier?: "hero" | "primary" | "secondary";
34+
/** Concrete output promise with quantities, e.g. "5 search wedges + page angles" */
35+
outputPromise?: string;
3236
}
3337

3438
// ---------------------------------------------------------------------------
@@ -75,6 +79,7 @@ export function resolveIcon(category: string): LucideIcon {
7579
// ---------------------------------------------------------------------------
7680

7781
const VALID_CATEGORIES = new Set<string>(["conversion", "distribution", "growth", "messaging", "research", "seo"]);
82+
const VALID_TIERS = new Set<string>(["hero", "primary", "secondary"]);
7883

7984
function isValidSuggestedAction(item: unknown): item is SuggestedActionData {
8085
if (typeof item !== "object" || item === null) return false;
@@ -87,7 +92,9 @@ function isValidSuggestedAction(item: unknown): item is SuggestedActionData {
8792
typeof obj.category === "string" && VALID_CATEGORIES.has(obj.category) &&
8893
(obj.skills === undefined || (Array.isArray(obj.skills) && obj.skills.every((s: unknown) => typeof s === "string"))) &&
8994
typeof obj.prompt === "string" && obj.prompt.length > 0 &&
90-
(obj.outputType === undefined || (typeof obj.outputType === "string" && obj.outputType.length > 0))
95+
(obj.outputType === undefined || (typeof obj.outputType === "string" && obj.outputType.length > 0)) &&
96+
(obj.tier === undefined || (typeof obj.tier === "string" && VALID_TIERS.has(obj.tier))) &&
97+
(obj.outputPromise === undefined || (typeof obj.outputPromise === "string" && obj.outputPromise.length > 0))
9198
);
9299
}
93100

@@ -143,7 +150,7 @@ export function toActionCard(data: SuggestedActionData): ActionCard {
143150

144151
const FIXED_CARD_TITLES = starterActions.map((a) => `- ${a.title}`).join("\n");
145152

146-
export const SUGGESTED_ACTIONS_PROMPT = `You are an expert startup marketing strategist. Your job is to suggest 2-3 highly specific, actionable marketing tasks for this particular business based on the workspace analysis.
153+
export const SUGGESTED_ACTIONS_PROMPT = `You are an expert startup marketing strategist. Your job is to suggest 4-6 high-leverage, company-specific marketing jobs for this particular business based on the workspace analysis.
147154
148155
Read the workspace context files — PRODUCT.md, MARKET.md, and GROWTH.md — to understand this business deeply.
149156
@@ -159,20 +166,44 @@ AVAILABLE SKILL CATALOG — reference these skill IDs in your suggestions:
159166
- Sales & GTM: revops, sales-enablement, competitor-alternatives
160167
- Foundation: product-marketing-context
161168
162-
Analyze the workspace context, consider which skills would be most impactful for this business, and suggest 2-3 action cards. Each card should represent a concrete marketing task that produces a specific deliverable.
169+
LEVERAGE-FIRST GENERATION CRITERIA — prioritize jobs that are:
170+
- **Outside-in**: discover unknown opportunities about markets, search demand, competitors, channels, proof, or distribution — things the founder cannot see from inside the product
171+
- **Revenue-adjacent**: tied to acquisition, conversion, trust, or demand generation — not internal cleanup or process work
172+
- **Unknown-before-use**: reveal something the founder does not already know — the output should surprise, not just confirm
173+
- **Hard-to-do-manually**: benefit significantly from company context + workflow packaging — not something achievable via a blank ChatGPT prompt
174+
175+
A strong first-run job should meet at least 3 of 4 criteria above.
176+
177+
DEMOTION RULES — avoid leading with these types of jobs:
178+
- Packaging work the founder already understands (e.g., rewriting copy they wrote)
179+
- Internal cleanup or "good hygiene" tasks (e.g., meta tag fixes, analytics setup)
180+
- Generic rewrites achievable via blank chat (e.g., "rewrite homepage hero" without strategic edge)
181+
- Incremental work that feels like maintenance rather than discovery
182+
- Jobs whose output the founder could mostly predict before running them
183+
184+
These can appear as secondary jobs but should never dominate the top recommendations.
185+
186+
OUTPUT PROMISE RULES — each job must include a concrete outputPromise with:
187+
- Specific quantities (e.g., "5", "20", "3-5")
188+
- Tangible deliverable types (e.g., "search wedges + page angles", "communities + posting angles", "proof pages from product data")
189+
- Never use abstract terms like "analysis", "strategy help", "growth recommendations", or "workflow result"
190+
191+
TIERING INSTRUCTIONS — self-rank each job:
192+
- "hero" (exactly 1): the single highest-leverage, most novel job — the best first thing to do for this company. Must feel like discovery, not packaging.
193+
- "primary" (2-3): strong follow-up jobs that are still high-value and varied across different marketing dimensions
194+
- "secondary" (1-2): useful concrete jobs that are less essential as first-proof actions — packaging or optimization work lives here
163195
164196
IMPORTANT CONSTRAINTS:
165197
- The following action cards ALREADY exist. Do NOT suggest duplicates or variations of these:
166198
${FIXED_CARD_TITLES}
167199
- Each suggestion must be DIFFERENT from the above — offer new angles based on what you learned about THIS specific business
168-
- Titles must imply a concrete result (e.g., "Draft comparison page vs [Competitor]", "Write launch post for r/[specific subreddit]")
200+
- Titles must imply a concrete result (e.g., "Find 5 search wedges this product can own", "Map 20 communities where ideal users hang out")
169201
- Do NOT use vague titles like "Improve marketing", "Build strategy", "Optimize funnel"
170202
- Prompts must reference PRODUCT.md, MARKET.md, and GROWTH.md
171203
- Categories must be one of: "conversion", "distribution", "growth", "messaging", "research", "seo"
172204
- Skills must be an array of skill IDs from the catalog above — pick the 1-3 most relevant skills for each action
173-
- Each suggestion must produce a concrete deliverable
174205
175-
Return ONLY a JSON array with 2-3 objects. No other text, no markdown fences, no explanation.
206+
Return ONLY a JSON array with 4-6 objects. No other text, no markdown fences, no explanation.
176207
177208
Each object must have these exact fields:
178209
{
@@ -183,31 +214,73 @@ Each object must have these exact fields:
183214
"category": "conversion" | "distribution" | "growth" | "messaging" | "research" | "seo",
184215
"skills": ["skill-id-1", "skill-id-2"],
185216
"prompt": "Full detailed prompt that will be sent to the AI agent. Must reference PRODUCT.md, MARKET.md, and GROWTH.md workspace files. Must produce a concrete, useful output.",
186-
"outputType": "Short deliverable label (under 30 chars) — e.g. '3 hero rewrites', 'Launch copy bundle', 'SEO opportunity map'. Must describe the tangible output the user receives."
217+
"outputType": "Short deliverable label (under 30 chars) — e.g. '3 hero rewrites', 'Launch copy bundle', 'SEO opportunity map'",
218+
"tier": "hero" | "primary" | "secondary",
219+
"outputPromise": "Concrete deliverable with quantities — e.g. '5 search wedges + page angles', '20 communities + posting angles', '5 proof pages from product data'"
187220
}
188221
189-
EXAMPLES OF GOOD SUGGESTIONS (for a product analytics tool):
222+
EXAMPLES OF STRONG LEVERAGE-FIRST SUGGESTIONS (for a product analytics tool):
190223
[
191224
{
192-
"id": "build-referral-program",
193-
"title": "Design a referral program",
194-
"promise": "Get a complete referral program with incentives, mechanics, and tracking",
195-
"description": "Designs a referral program tailored to this product using viral loop mechanics, incentive structures, and tracking setup.",
196-
"category": "growth",
197-
"skills": ["referral-program"],
198-
"prompt": "Read PRODUCT.md, MARKET.md, and GROWTH.md. Design a complete referral program with incentive structure, viral mechanics, referral flow, tracking setup, and launch plan.",
199-
"outputType": "Referral program blueprint"
225+
"id": "find-search-wedges",
226+
"title": "Find 5 search wedges you can own",
227+
"promise": "Discover high-intent search opportunities your competitors are missing",
228+
"description": "Analyzes search demand around product analytics pain points, identifies 5 specific keyword wedges where this product can realistically rank, and produces page angles for each wedge.",
229+
"category": "seo",
230+
"skills": ["seo-audit", "content-strategy"],
231+
"prompt": "Read PRODUCT.md, MARKET.md, and GROWTH.md. Identify 5 specific search wedges this product can own. For each wedge, provide the target keyword cluster, estimated intent, current competitor coverage, and a concrete page angle this product should build.",
232+
"outputType": "SEO wedge map",
233+
"tier": "hero",
234+
"outputPromise": "5 search wedges + page angles with competitor gaps"
200235
},
201236
{
202-
"id": "draft-comparison-vs-mixpanel",
203-
"title": "Draft comparison page vs Mixpanel",
204-
"promise": "Create a detailed comparison page highlighting your advantages over Mixpanel",
205-
"description": "Analyzes Mixpanel's positioning and creates a comparison page draft that highlights your unique advantages, addresses common switching objections, and targets users searching for alternatives.",
237+
"id": "build-proof-pages",
238+
"title": "Turn product data into 5 proof pages",
239+
"promise": "Convert your product's unique data into trust-building assets",
240+
"description": "Identifies 5 proof page opportunities from product data (benchmarks, case patterns, usage insights) and produces outlines that turn internal knowledge into external trust signals.",
241+
"category": "conversion",
242+
"skills": ["copywriting", "page-cro"],
243+
"prompt": "Read PRODUCT.md, MARKET.md, and GROWTH.md. Identify 5 proof page opportunities where this product's data or usage patterns can be turned into trust-building content. For each, produce a page outline with headline, key data points, and conversion angle.",
244+
"outputType": "Proof page bundle",
245+
"tier": "primary",
246+
"outputPromise": "5 proof page outlines with data angles"
247+
},
248+
{
249+
"id": "map-competitor-comparison-angles",
250+
"title": "Generate 5 comparison pages you can win",
251+
"promise": "Find the competitor matchups where you have the strongest positioning",
252+
"description": "Analyzes the competitive landscape to find 5 specific comparison page opportunities where this product has clear advantages, and drafts positioning angles for each.",
206253
"category": "messaging",
207254
"skills": ["competitor-alternatives", "copywriting"],
208-
"prompt": "Read PRODUCT.md, MARKET.md, and GROWTH.md. Then create a detailed comparison page draft of our product vs Mixpanel...",
209-
"outputType": "Comparison page draft"
255+
"prompt": "Read PRODUCT.md, MARKET.md, and GROWTH.md. Identify 5 competitor comparison page opportunities. For each, explain why this product wins the matchup, draft the key positioning angles, and outline the page structure.",
256+
"outputType": "Comparison page backlog",
257+
"tier": "primary",
258+
"outputPromise": "5 comparison pages + positioning angles"
259+
},
260+
{
261+
"id": "find-launch-communities",
262+
"title": "Map 20 communities where ideal users gather",
263+
"promise": "Find the exact online spaces where your target users already hang out",
264+
"description": "Discovers 20 online communities (subreddits, Slack groups, Discord servers, forums, newsletters) where this product's ideal users actively discuss relevant problems, with posting angle suggestions.",
265+
"category": "distribution",
266+
"skills": ["marketing-ideas", "social-content"],
267+
"prompt": "Read PRODUCT.md, MARKET.md, and GROWTH.md. Find 20 specific online communities where this product's ideal users gather. For each community, provide the name, URL, estimated relevance, and 2-3 posting angles tailored to that community's norms.",
268+
"outputType": "Community shortlist",
269+
"tier": "primary",
270+
"outputPromise": "20 communities + posting angles for each"
271+
},
272+
{
273+
"id": "create-outbound-angles",
274+
"title": "Draft 4 cold outbound angles + sequences",
275+
"promise": "Get ready-to-send outbound sequences targeting your best buyer signals",
276+
"description": "Identifies 4 distinct buyer signals for this product and drafts a cold outbound email sequence for each, personalized to the product's positioning and ICP.",
277+
"category": "growth",
278+
"skills": ["cold-email", "email-sequence"],
279+
"prompt": "Read PRODUCT.md, MARKET.md, and GROWTH.md. Identify 4 buyer signals that indicate high purchase intent. For each signal, draft a 3-email cold outbound sequence with subject lines, hooks, and CTAs.",
280+
"outputType": "Outbound sequence pack",
281+
"tier": "secondary",
282+
"outputPromise": "4 outbound angles + 3-email sequences each"
210283
}
211284
]
212285
213-
Now analyze the workspace files and suggest 2-3 actions tailored to THIS business.`;
286+
Now analyze the workspace files and suggest 4-6 actions tailored to THIS business.`;

0 commit comments

Comments
 (0)