| name | onboarding |
| description | Optimizar onboarding flow. Two distinct paths: NEW CHANNEL (user has no YouTube channel or a brand new one with no data) and EXISTING CHANNEL (user connects a live channel with history). New channel path runs a live niche intelligence report โ current trends, CTR benchmarks, CPM ranges, saturation levels โ and recommends niches based on real data. Existing channel path audits the connected channel's videos, keywords, and watch time distribution, identifies the channel's actual niche identity, then compares against the user's chosen niche and fires a conflict notification if there is a meaningful mismatch. Read this skill when building the onboarding flow, niche selection, channel audit, niche conflict detection, or the niche recommendation report.
|
Onboarding โ Niche Intelligence & Channel Audit
Two Doors
User lands on onboarding
โ
โผ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ Do you have a YouTube channel? โ
โ โ
โ [I'm starting fresh] โ
โ [I have an existing channel] โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ
โผ โผ
NEW CHANNEL EXISTING CHANNEL
FLOW FLOW
(Niche Scout) (Channel Audit)
FLOW 1 โ New Channel: Niche Scout
User has no channel or a blank one. They need our team to tell them
where the opportunity is right now. Not opinions. Live data.
What Niche Scout Does
Rex โ scans current trending topics across all niches
SNIPER โ overlays CPM data, market traction, geo opportunity
Oracle โ contributes historical performance patterns per niche
Sonnet โ synthesises into a ranked niche recommendation report
Trigger
Button on onboarding screen:
"Find the best niche for me right now โ"
Sub-label: "Our team scans current trends, CTR benchmarks,
and CPM data to find where the opportunity is today."
The Scout Run
export const TRACKABLE_NICHES = [
{ id: "tech", label: "Tech & AI", icon: "๐ป" },
{ id: "finance", label: "Finance & Investing", icon: "๐" },
{ id: "gaming", label: "Gaming", icon: "๐ฎ" },
{ id: "beauty", label: "Beauty & Lifestyle", icon: "โจ" },
{ id: "health", label: "Health & Fitness", icon: "๐ช" },
{ id: "food", label: "Food & Cooking", icon: "๐ณ" },
{ id: "sports", label: "Sports", icon: "โฝ" },
{ id: "f1", label: "F1 & Motorsport", icon: "๐๏ธ" },
{ id: "business", label: "Business & Startups", icon: "๐" },
{ id: "science", label: "Science & Space", icon: "๐ฌ" },
{ id: "travel", label: "Travel", icon: "โ๏ธ" },
{ id: "entertainment", label: "Entertainment & Pop", icon: "๐ฌ" },
] as const;
export interface NicheIntelligence {
nicheId: string;
label: string;
avgCTR: number;
cpmRange: { min: number; max: number };
saturationLevel: "LOW" | "MEDIUM" | "HIGH";
growthVelocity: "ACCELERATING" | "STABLE" | "DECLINING";
breakoutChannelsLast60Days: number;
topTrendingTopics: string[];
avgWatchTime: number;
searchVolumeGrowth: string;
opportunityScore: number;
whyNow: string;
bestForNewChannel: boolean;
multiNicheCompatibility: string[];
}
export async function runNicheScout(): Promise<NicheScoutReport> {
const trendingByNiche = await Promise.all(
TRACKABLE_NICHES.map(niche => scanNicheTrends(niche.id))
);
const marketData = await Promise.all(
TRACKABLE_NICHES.map(niche => getSniperNicheData(niche.id))
);
const oracleContext = await queryOracleKnowledge(
"Which YouTube niches are showing the strongest growth velocity " +
"and best CPM performance right now? Which are saturating?",
"CONTENT_TREND_SIGNALS"
);
const response = await fetch("https://api.anthropic.com/v1/messages", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
model: "claude-sonnet-4-20250514",
max_tokens: 3000,
system: `You are the Optimizar team's niche intelligence analyst.
A new creator is deciding where to start their YouTube channel.
Your job: give them the most useful, data-backed niche
recommendation possible. Be specific. Use the actual numbers.
Don't be generic. Tell them what is working RIGHT NOW.
Oracle context: ${oracleContext}`,
messages: [{
role: "user",
content: `Analyse this niche data and return a ranked niche report.
Trending by niche: ${JSON.stringify(trendingByNiche)}
Market data: ${JSON.stringify(marketData)}
Return JSON:
{
"rankedNiches": NicheIntelligence[], // sorted by opportunityScore desc
"topPick": string, // single best niche right now
"topPairings": [ // best 2-niche and 3-niche combos
{ "niches": string[], "reason": string, "combinedScore": number }
],
"marketSummary": string, // 2-sentence overall landscape summary
"generatedAt": string // ISO timestamp
}`
}]
})
});
const data = await response.json();
const report = JSON.parse(
data.content[0].text.replace(/```json|```/g, "").trim()
);
await cacheNicheScoutReport(report);
return report;
}
Niche Scout UI
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ NICHE INTELLIGENCE REPORT Live ยท Updated 2 hrs ago โ
โ Based on current trends, CTR data, and CPM benchmarks โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ ๐ TOP OPPORTUNITY RIGHT NOW โ
โ โ
โ Tech & AI Score: 91/100 โ
โ โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ โ
โ โ
โ Avg CTR: 7.2% CPM: $18โ34 Saturation: LOW Growth: โโ โ
โ "AI tool comparisons and LLM breakdowns are getting 40%+ โ
โ higher CTR than any other tech sub-niche right now." โ
โ Top trending: GPT-5 release ยท DeepSeek R2 ยท Claude 4 vs Gemini โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ ALL NICHES โ
โ โ
โ ๐ป Tech & AI 91 โโโโโโโโโโโโโโโโโโโโ 7.2% CTR $18โ34โ
โ ๐ Finance 87 โโโโโโโโโโโโโโโโโโโ 6.8% CTR $22โ41โ
โ โจ Beauty 79 โโโโโโโโโโโโโโโโโ 5.1% CTR $12โ19โ
โ ๐ Business 76 โโโโโโโโโโโโโโโโ 6.1% CTR $19โ28โ
โ ๐ฌ Science 71 โโโโโโโโโโโโโโโ 5.8% CTR $14โ22โ
โ ๐๏ธ F1 & Motorsport 68 โโโโโโโโโโโโโโ 6.4% CTR $16โ24โ
โ ๐ช Health 65 โโโโโโโโโโโโโ 4.9% CTR $11โ18โ
โ ๐ฎ Gaming 61 โโโโโโโโโโโโ 4.2% CTR $ 8โ14โ
โ โฝ Sports 58 โโโโโโโโโโโ 5.5% CTR $10โ16โ
โ ๐ณ Food 54 โโโโโโโโโโ 4.1% CTR $ 9โ13โ
โ โ๏ธ Travel 49 โโโโโโโโโ 3.8% CTR $ 8โ12โ
โ ๐ฌ Entertainment 44 โโโโโโโโ 4.4% CTR $ 7โ11โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ BEST COMBINATIONS โ
โ โ
โ โฆ Tech + Finance Score: 94 "Shared audience of 25โ40yr โ
โ professionals. AI investing โ
โ is a breakout sub-niche." โ
โ โ
โ Tech + Business Score: 89 "Startup & SaaS founders โ
โ watch both. High CPM overlap." โ
โ โ
โ Finance + Business Score: 85 "Strong evergreen potential. โ
โ Lower viral ceiling." โ
โ โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ [Select Tech & AI โ] [Select Tech + Finance โ] [Custom] โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Design notes:
- Score bars animate in left to right on page load, staggered 80ms per row
- Top opportunity card pulses amber border on load โ draws eye immediately
- Combinations section shows pairing logic in plain English โ not just numbers
- "Live ยท Updated X hrs ago" timestamp โ builds trust in data freshness
- Refresh button triggers a new Rex scan (rate limited to once per hour)
FLOW 2 โ Existing Channel: Channel Audit
User connects their YouTube channel. We read their data.
Our team figures out what their channel actually is โ then compares
against what they want it to be.
What Channel Audit Does
YouTube Analytics API โ pulls videos, keywords, watch time, CTR per video
ARIA โ identifies content distribution across themes
Oracle โ cross-references against niche pattern library
Sonnet 4 โ generates channel identity report
Opus 4 (if conflict) โ writes the conflict notification
The Audit Run
export interface ChannelIdentityReport {
dominantNiche: string;
dominantNicheConfidence: number;
secondaryNiche: string | null;
contentDistribution: {
niche: string;
videoCount: number;
watchTimePercent: number;
avgCTR: number;
}[];
totalVideos: number;
totalWatchHours: number;
avgCTR: number;
avgRetention: number;
subscriberGrowthTrend: "GROWING" | "FLAT" | "DECLINING";
bestPerformingNiche: string;
bestPerformingFormat: string;
topKeywords: { keyword: string; searchVolume: string; avgCTR: number }[];
identityClarity: "CLEAR" | "MIXED" | "SCATTERED";
identitySummary: string;
strengthsToKeep: string[];
weaknessesToAddress: string[];
}
export async function runChannelAudit(
userId: string
): Promise<ChannelIdentityReport> {
const [videos, analytics, keywords] = await Promise.all([
getChannelVideos(userId, { limit: 100 }),
getChannelAnalytics(userId, { days: 365 }),
getChannelKeywords(userId),
]);
const distribution = await ariaContentDistribution(videos, analytics);
const oracleContext = await queryOracleKnowledge(
`What defines a ${distribution.topNiche} channel? What are the
clearest signals that a channel belongs to this niche?`,
"CONTENT_TREND_SIGNALS"
);
const response = await fetch("https://api.anthropic.com/v1/messages", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
model: "claude-sonnet-4-20250514",
max_tokens: 2000,
system: `You are the Optimizar team analysing a YouTube channel's
identity based on real data. Be precise and direct.
You are helping the creator understand what their channel
actually is โ so they can make informed decisions.
Oracle context: ${oracleContext}`,
messages: [{
role: "user",
content: `Analyse this channel data and return a ChannelIdentityReport.
Videos: ${JSON.stringify(videos.slice(0, 50))}
Analytics: ${JSON.stringify(analytics)}
Keywords: ${JSON.stringify(keywords)}
ARIA distribution: ${JSON.stringify(distribution)}
Be specific about the dominant niche. Use real numbers.
Return JSON only.`
}]
})
});
const data = await response.json();
return JSON.parse(data.content[0].text.replace(/```json|```/g, "").trim());
}
Channel Audit UI
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ YOUR CHANNEL REPORT 247 videos analysed โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโค
โ โ
โ CHANNEL IDENTITY โ
โ โโโโโโโโโโโโโโโโโ โ
โ Your channel is a: Cooking & Food channel โ
โ Identity clarity: CLEAR (87% confidence) โ
โ โ
โ "73% of your watch time comes from recipe and cooking tutorial โ
โ content. Your top 5 performing videos are all food-related. โ
โ Your audience has a clear expectation." โ
โ โ
โ CONTENT DISTRIBUTION โ
โ Cooking & Food โโโโโโโโโโโโโโโโโโโโโโโ 73% 4.8% CTR โ
โ Lifestyle โโโโโโโโ 18% 3.1% CTR โ
โ Vlogs โโโ 9% 2.4% CTR โ
โ โ
โ TOP KEYWORDS โ
โ "easy dinner recipes" ยท 4.2% CTR ยท High volume โ
โ "meal prep ideas" ยท 5.1% CTR ยท High volume โ
โ "30 minute meals" ยท 6.3% CTR ยท Medium volume โ
โ โ
โ STRENGTHS โ
โ โ Strong recipe tutorial retention (avg 52%) โ
โ โ Meal prep content drives subscriber conversion โ
โ โ
โ AREAS TO ADDRESS โ
โ โ Lifestyle content underperforms โ consider cutting โ
โ โ No consistent upload schedule visible in data โ
โ โ
โ [This looks right โ continue โ] [My channel is changing] โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
Niche Conflict Detection โ The "Bro" Moment
When the user's selected niche conflicts meaningfully with their
channel's audited identity, Jason fires the conflict notification.
Not a blocker. A checkpoint.
Conflict Scoring
export interface NicheConflict {
detected: boolean;
severity: "MILD" | "MODERATE" | "STRONG";
channelNiche: string;
selectedNiche: string;
overlapScore: number;
watchTimeAtRisk: number;
message: string;
options: ConflictOption[];
}
export type ConflictOption =
| "PROCEED_ANYWAY"
| "RUN_BOTH"
| "GRADUAL_TRANSITION"
| "CHANGE_SELECTION"
export const NICHE_OVERLAP: Record<string, Record<string, number>> = {
food: { food: 100, lifestyle: 65, health: 55, travel: 40, beauty: 30,
finance: 5, tech: 5, gaming: 5, sports: 10 },
tech: { tech: 100, finance: 60, business: 65, science: 55, gaming: 45,
food: 5, beauty: 5, lifestyle: 15, sports: 10 },
gaming: { gaming: 100, tech: 45, entertainment: 55, sports: 30,
food: 5, beauty: 5, finance: 10 },
finance: { finance: 100, business: 75, tech: 60, science: 25,
food: 5, gaming: 10, beauty: 5 },
beauty: { beauty: 100, lifestyle: 80, health: 60, food: 35,
travel: 45, tech: 5, gaming: 5, finance: 5 },
sports: { sports: 100, f1: 60, gaming: 30, health: 40, entertainment: 35,
food: 10, tech: 10 },
};
export function detectConflict(
audit: ChannelIdentityReport,
selectedNiches: string[]
): NicheConflict {
const channelNiche = audit.dominantNiche.toLowerCase();
const primarySelected = selectedNiches[0].toLowerCase();
const overlapScore = Math.max(
...selectedNiches.map(n =>
NICHE_OVERLAP[channelNiche]?.[n.toLowerCase()] ?? 20
)
);
const watchTimeAtRisk = audit.contentDistribution
.filter(d => !selectedNiches.some(n =>
d.niche.toLowerCase().includes(n.toLowerCase())
))
.reduce((sum, d) => sum + d.watchTimePercent, 0);
if (overlapScore >= 60) {
return { detected: false, severity: "MILD", overlapScore,
channelNiche, selectedNiche: primarySelected,
watchTimeAtRisk, message: "", options: [] };
}
const severity = overlapScore < 20 ? "STRONG"
: overlapScore < 40 ? "MODERATE"
: "MILD";
const message = buildConflictMessage(
audit, selectedNiches, severity, overlapScore, watchTimeAtRisk
);
return {
detected: true,
severity,
channelNiche,
selectedNiche: primarySelected,
overlapScore,
watchTimeAtRisk,
message,
options: buildOptions(severity, channelNiche, selectedNiches),
};
}
async function buildConflictMessage(
audit: ChannelIdentityReport,
selectedNiches: string[],
severity: string,
overlapScore: number,
watchTimeAtRisk: number,
): Promise<string> {
const response = await fetch("https://api.anthropic.com/v1/messages", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({
model: "claude-opus-4-20250514",
max_tokens: 300,
system: `You are Jason โ the project manager at Optimizar.
A user is about to make a significant niche change on their
YouTube channel. Your job is to flag it clearly, honestly,
and with personality. Not preachy. Not corporate.
Direct, warm, and a little bit like a smart friend
who knows YouTube. Max 3 sentences.
Severity: ${severity}. Keep the tone proportional โ
MILD is a gentle heads-up, STRONG is a clear flag.`,
messages: [{
role: "user",
content: `Channel identity: ${audit.dominantNiche}
(${audit.totalVideos} videos, ${audit.totalWatchHours} watch hours)
User is selecting: ${selectedNiches.join(" + ")}
Overlap score: ${overlapScore}/100
Watch time at risk: ${watchTimeAtRisk}%
Write the conflict notification. Be specific with the numbers.
Don't use "bro" literally โ but match that energy.`
}]
})
});
const data = await response.json();
return data.content[0].text.trim();
}
Conflict Notification UI
STRONG conflict example (Food โ Gaming):
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ โ ๏ธ HEADS UP FROM JASON โ
โ โ
โ "You've built 2,300 watch hours in cooking content โ that's โ
โ real equity. Gaming has about a 5% audience overlap with โ
โ food creators, so you'd essentially be starting over. โ
โ Are you pivoting or want to run both?" โ
โ โ
โ [I know โ go gaming] [Run both niches] [Gradual transition] โ
โ [Actually, let me reconsider] โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
MODERATE conflict example (Food โ Health):
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
โ ๐ก QUICK NOTE FROM JASON โ
โ โ
โ "Your cooking audience and health audiences do overlap โ โ
โ about 55%. Healthy recipes could be your bridge. Just worth โ
โ knowing 45% of your current watch time may not follow." โ
โ โ
โ [Got it โ continue] [Show me the bridge strategy] โ
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
MILD (no conflict) โ no notification. Just proceed silently.
Design notes for both notifications:
- STRONG: amber border, warning icon, 4 options
- MODERATE: subtle blue border, info icon, 2 options
- MILD: no notification โ smooth proceed, no friction
- All options are non-blocking โ user always has the final word
- "Show me the bridge strategy" โ Jason generates a 30-day
transition plan showing how to blend content across niches
Confidence Score Evaluation
Before a user commits to their niche+mode combination, the system calls Bedrock Haiku to evaluate and return a confidence score (0โ100). NOT hardcoded โ genuinely evaluated per combination every time.
User can select multiple niches (e.g. AI + Finance + Racing) and set mode per niche independently:
- Face โ SkyReels avatar presenter
- Faceless โ TONY + Wan2.2 + ElevenLabs only
- Let RRQ Decide โ Regum picks per video based on content type
Types
interface ConfidenceEvalRequest {
channelId: string;
niches: {
niche: string;
mode: 'FACE' | 'FACELESS' | 'LET_RRQ_DECIDE';
}[];
channelType: 'EDUCATIONAL' | 'ENTERTAINMENT' | 'NEWS' | 'MIXED';
}
interface ConfidenceEvalResult {
overall: number;
label: 'EXCELLENT' | 'GOOD' | 'MODERATE' | 'LOW' | 'NOT_RECOMMENDED';
perNiche: {
niche: string;
mode: string;
score: number;
reasoning: string;
risks: string[];
}[];
crossNicheCoherence: number;
suggestions: string[];
risks: string[];
}
Seven Evaluation Dimensions
Haiku evaluates these dimensions on every call. NOT hardcoded โ passed as evaluation criteria in the system prompt so Haiku reasons about them freshly for each niche+mode combination:
- Content producibility โ can TONY + Wan2.2 handle this niche visually without a face?
- Niche-mode fit โ does faceless/face work for this content type?
- Market saturation โ how crowded is this niche right now?
- Cross-niche coherence โ do these niches make sense on one channel?
- AI detection risk โ how likely is YouTube to flag this combo?
- Revenue potential โ CPM estimates per niche (finance > racing > AI news typical)
- Audience overlap โ do these audiences overlap or fragment?
Implementation
export async function evaluateChannelConfidence(
req: ConfidenceEvalRequest
): Promise<ConfidenceEvalResult> {
const cached = await getConfidenceCache(req.channelId);
if (cached && !cacheExpired(cached)) return cached;
const bedrockClient = new BedrockRuntimeClient({ region: process.env.AWS_REGION });
const systemPrompt = `You are the Optimizar confidence analysis system.
A creator is selecting niches and production modes for their YouTube channel.
Evaluate this combination on seven dimensions:
1. Content producibility โ can TONY + Wan2.2 produce this niche visually without a face?
2. Niche-mode fit โ does the chosen face/faceless mode suit this content type?
3. Market saturation โ how competitive is this niche at this moment?
4. Cross-niche coherence โ do these niches make sense together on one channel?
5. AI detection risk โ how likely is YouTube to flag this content combination?
6. Revenue potential โ realistic CPM range for this niche mix?
7. Audience overlap โ do these audiences reinforce or fragment each other?
Be specific and honest. Score each niche independently, then produce an overall score.
Return JSON only โ no prose outside the JSON block.`;
const command = new InvokeModelCommand({
modelId: process.env.BEDROCK_HAIKU_MODEL ?? "anthropic.claude-haiku-4-5-20251001",
body: JSON.stringify({
anthropic_version: "bedrock-2023-05-31",
max_tokens: 1500,
system: systemPrompt,
messages: [{
role: "user",
content: `Channel type: ${req.channelType}
Niche+mode selections: ${JSON.stringify(req.niches, null, 2)}
Evaluate this combination and return a ConfidenceEvalResult JSON object.
Overall score 0โ100. Per-niche score 0โ100 each.
Include concrete suggestions if score < 90 (e.g. "Switch Racing to Faceless for +7 points").
Include honest risks at the channel level.`
}]
}),
contentType: "application/json",
accept: "application/json",
});
const response = await bedrockClient.send(command);
const body = JSON.parse(new TextDecoder().decode(response.body));
const result: ConfidenceEvalResult = JSON.parse(
body.content[0].text.replace(/```json|```/g, "").trim()
);
await setConfidenceCache(req.channelId, result);
return result;
}
Caching
DynamoDB table: channel-confidence
- PK: channelId
- TTL: 24 hours
- Re-evaluate anytime user changes niche or mode selection
- Never block re-evaluation โ it is a Haiku call (~1โ2 seconds)
Escalation
If Haiku call fails twice โ show cached result with "Last evaluated X hours ago" label.
If no cache โ show manual checklist prompt, do not block onboarding.
Reference: ESCALATION_POLICIES.CONFIDENCE_SCORE in skills/escalation/SKILL.md
Integration Point
Called from: onboarding UI after niche+mode selection, before [ACCEPT] button activates.
Also callable from: channel settings page anytime user wants to re-evaluate.
Post-Onboarding โ Niche Written to Channel Settings
Whichever flow completes, the final niche selection is written to
channel-settings DynamoDB table and broadcast to all agents.
export async function completeOnboarding(
userId: string,
selectedNiches: string[],
channelMode: "OPEN" | "NICHE_LOCKED" | "MULTI_NICHE",
conflictResolution?: ConflictOption,
): Promise<void> {
await updateChannelSettings(userId, {
niches: selectedNiches,
channelMode,
conflictResolution: conflictResolution ?? null,
onboardingCompletedAt: new Date().toISOString(),
});
await writeToAgentMessages({
type: "CHANNEL_MODE_SET",
from: "ONBOARDING",
to: "THE_LINE",
payload: {
niches: selectedNiches,
channelMode,
missionSuccessOdds: channelMode === "MULTI_NICHE" ? 95 : 93,
}
});
await writeToAgentMessages({
type: "FIRST_SPRINT_TRIGGER",
from: "ONBOARDING",
to: "THE_LINE",
payload: { userId, niches: selectedNiches, phase: "COLD_START" },
});
if (conflictResolution) {
await writeToAgentMessages({
type: "MEMORY_INJECTION",
from: "ONBOARDING",
to: "ZEUS",
payload: {
source: "CHANNEL_AUDIT",
instruction: `Channel has history in ${selectedNiches.join(" + ")}.
User resolved conflict as: ${conflictResolution}.
Factor this into all early content decisions.`,
}
});
}
}
Onboarding Flow Summary
NEW CHANNEL EXISTING CHANNEL
โโโโโโโโโโโ โโโโโโโโโโโโโโโโ
1. Click "Find my niche" 1. Connect YouTube channel
2. Rex + SNIPER + Oracle scan 2. Audit runs (30 seconds)
3. Niche Scout report loads 3. Channel Identity report loads
4. User sees ranked niches + pairs 4. User sees what their channel is
5. User selects 1, 2, or 3 niches 5. User selects desired niches
6. Probability bar shows 91โ95% 6. Conflict detector runs
7. Confirm โ onboarding complete 7a. No conflict โ proceed
7b. Conflict โ Jason notification
7c. User chooses resolution
8. Onboarding complete
Both flows end at the same place:
Channel settings written, agents briefed, first sprint created,
Mission Control dashboard opens, Jason's Day 1 sprint plan ready.
New DynamoDB Tables
channel-audit PK: userId
fields: dominantNiche, confidence, distribution[],
topKeywords[], identityClarity, identitySummary,
auditRunAt, videosAnalysed
channel-confidence PK: channelId
fields: overall, label, perNiche[], crossNicheCoherence,
suggestions[], risks[], evaluatedAt
TTL: 24 hours โ re-evaluates automatically on change
Environment Variables
Confidence Scoring โ Dynamic Niche + Mode Evaluation
After user selects niches and mode per niche, before they proceed:
Trigger: user clicks [EVALUATE] button
What fires:
- Bedrock Haiku call (fast, ~2โ3 seconds)
- Input: niche combination, mode per niche, channel type
- Haiku evaluates 7 dimensions dynamically (NOT hardcoded scores):
1. Content producibility Can TONY + Wan2.2 handle this niche visually?
2. Niche-mode fit Does faceless/face work for this content type?
3. Market saturation How crowded is this niche right now?
4. Cross-niche coherence Do these niches make sense on one channel?
5. AI detection risk How likely is YouTube to flag this combo?
6. Revenue potential CPM estimates per niche
7. Audience overlap Do these audiences overlap or fragment?
Output:
- Overall score (0โ100)
- Per-niche breakdown with score + label
- Risk warnings (specific, not generic)
- Suggestions (e.g. "switch Racing to faceless โ score goes from 67 to 89")
Caching:
- Result stored in
channel-confidence DynamoDB table (24h TTL)
- If user returns within 24h with same niche/mode combo โ show cached result
- Re-evaluate available any time via button
User can:
- Change any niche or mode and re-evaluate instantly
- No limit on evaluations
- Proceed with any score (not gated โ informational only)
Rex Warm-Up Sprint โ First Run Intelligence
Trigger: user completes onboarding and enters Rex Mode for the first time
What fires:
- Rex full signal scan (all 6 sources in parallel)
- Takes 5โ15 minutes โ do not skip or fake this
- Real market data, not placeholder scores
UI during scan:
- Rex warm-up sprint screen (see frontend-design-spec
## Rex Warm-Up Sprint UI)
- Live signal feed updating as each source completes
- Confidence score building in real time
Why it takes time (explain to user in UI):
- Rex is querying 6 live signal sources
- Building a real trend map for the niche
- First scan is the most thorough โ subsequent scans are incremental
On completion:
- Rex Topic Queue populated with ranked opportunities
- User sees ranked list with confidence scores and Rex reasoning
- User picks topic and clicks GO
Channel Tone Capture
Tone is captured as a single optional step after niche selection, before the confidence
evaluation fires. It is never a gate โ a user who skips it or selects "Not sure yet" proceeds
normally with tone defaulting to "hybrid".
Tone Step UI
How do you want your channel to feel?
โ Analytical โ Data-driven, credible, structured
โ Explanatory โ Educational, clear, accessible
โ Critical โ Opinion-led, contrarian, bold
โ Entertainment โ Engaging, story-driven, emotional
โ Hybrid โ Mix of the above (recommended for new channels)
โ Not sure yet โ We'll start neutral and learn over time
[SKIP โ] [CONTINUE โ]
"Not sure yet" maps to "hybrid". Both SKIP and "Not sure yet" are equivalent โ neither
blocks the user, neither requires a rationale.
DynamoDB Schema
Stored in user-settings under channelTone:
interface ChannelTone {
primary: "analytical" | "explanatory" | "critical" | "entertainment" | "hybrid";
secondary?: "analytical" | "explanatory" | "critical" | "entertainment";
confidence: number;
definedAt: "onboarding" | "evolved" | "user-set";
lastUpdatedAt: string;
}
definedAt: "onboarding" โ user selected at this step
definedAt: "evolved" โ Oracle suggested a refinement and user accepted
definedAt: "user-set" โ user manually changed in Settings after onboarding
Default if skipped: { primary: "hybrid", confidence: 0.3, definedAt: "onboarding" }
Downstream Injection
Once captured, channelTone is passed as a context field in:
- Muse's MuseBlueprint generation call
- Script writer system prompt addendum (DynamoDB hybrid prompt)
- SEO title generation (tone shapes title framing)
- Rex topic confidence scoring (topic-tone fit dimension)
No agent uses tone as a hard gate. It is a directional signal, not a filter.
Tone Evolution (Oracle + Zeus)
After 5+ videos are published, Oracle Domain 10 surfaces a tone refinement suggestion:
Based on your last 8 videos:
โข Analytical scripts averaged 68% retention
โข Explanatory scripts averaged 74% retention
Suggested tone update: Add "explanatory" as secondary tone.
[ACCEPT] [DISMISS]
User accepts โ channelTone.secondary updated, definedAt โ "evolved".
User dismisses โ Oracle does not re-suggest for 30 days.
Zeus logs the outcome as an episode to S3 (rrq-memory/episodes/oracle/).
Checklist
[ ] Create lib/onboarding/ folder
[ ] Create lib/onboarding/niche-scout.ts โ Rex + SNIPER + Oracle scan
[ ] Create lib/onboarding/channel-audit.ts โ YouTube data pull + ARIA analysis
[ ] Create lib/onboarding/conflict-detector.ts โ overlap matrix + Opus notification
[ ] Create lib/onboarding/confidence-eval.ts โ Bedrock Haiku 7-dimension scorer
[ ] Create lib/onboarding/complete.ts โ write settings, brief agents, start sprint
[ ] Create DynamoDB table: channel-audit
[ ] Create DynamoDB table: channel-confidence โ 24h TTL, PK: channelId
[ ] Create app/onboarding/ Next.js route
[ ] Create components/onboarding/
TwoDoors.tsx โ new vs existing channel choice
NicheScoutReport.tsx โ ranked niche cards with bars
NichePairings.tsx โ best combination cards
ChannelAuditReport.tsx โ identity report + distribution
NicheSelector.tsx โ multi-select with mode toggle + probability bar
NicheModeToggle.tsx โ per-niche Face/Faceless/Let RRQ radio group
ConflictNotification.tsx โ Jason's warning with resolution options
ConfidenceScoreCard.tsx โ overall + per-niche score bars with RE-EVALUATE
ToneSelector.tsx โ 5-option tone radio group + skip button
OnboardingComplete.tsx โ transition to Mission Control
[ ] Wire "Find my niche" button โ runNicheScout()
[ ] Wire YouTube connect โ runChannelAudit()
[ ] Wire niche selection โ detectConflict()
[ ] Wire niche+mode selection โ evaluateChannelConfidence() โ fires before ACCEPT
[ ] Wire RE-EVALUATE button โ evaluateChannelConfidence() with loading spinner
[ ] Wire ACCEPT button โ only enabled when confidence overall >= 40
[ ] Wire tone selection โ user-settings DynamoDB channelTone field (or default hybrid on skip)
[ ] Wire completion โ completeOnboarding() + Jason first sprint
[ ] Cache niche scout report โ 6 hour TTL
[ ] Cache confidence eval โ 24 hour TTL in channel-confidence DynamoDB
[ ] Rate limit niche scout refresh โ max once per hour per user
[ ] Test confidence eval: EXCELLENT combination (Finance Faceless + AI Faceless)
[ ] Test confidence eval: LOW combination (Beauty Face + Finance Faceless + Gaming Face)
[ ] Test confidence eval: Haiku failure ร 2 โ verify cached fallback with timestamp
[ ] Test confidence eval: no cache + Haiku down โ verify manual checklist prompt shown
[ ] Test ACCEPT button disabled when overall < 40
[ ] Test strong conflict (food โ gaming) โ verify Jason notification fires
[ ] Test mild overlap (tech โ finance) โ verify no notification
[ ] Test multi-niche selection โ verify probability bar animates to 95%
[ ] Test tone selection โ user-settings write verified
[ ] Test tone skip โ default hybrid written with confidence 0.3
[ ] Test "Not sure yet" โ maps to hybrid correctly
[ ] Test tone injection reaches Muse system prompt on first video
[ ] Test new channel flow end-to-end
[ ] Test existing channel flow end-to-end