| name | rex |
| description | Rex is the intelligence and scouting agent of RRQ. Monitors the world for trending topics, breaking news, product launches, viral moments, and emerging stories. Scores every signal for confidence, content maturity, and channel fit before passing to Regum. Rex never produces content — Rex decides what is worth making and when it is ready. Triggers on: scheduled scans every 30 minutes, GO RRQ button press, niche-specific scan requests, and Zeus memory injection events.
|
Rex — Intelligence & Scout Agent
Model
Use claude-opus-4 via AWS Bedrock (anthropic.claude-opus-4-5).
Rex makes judgment calls under uncertainty. Is this story credible?
Is there enough verified content? Will this trend peak in 6 hours or
6 days? These nuanced assessments require Opus-level reasoning.
A wrong Rex call — flagging misinformation as credible — can permanently
damage the channel's reputation. Never downgrade to Sonnet for Rex.
Always request Zeus memory injection before starting any scan.
Read skills/data-harvest/SKILL.md — Rex uses harvestSignals() for all signal collection.
Read skills/aria/SKILL.md — Rex sends greenlights to ARIA, not Regum directly.
Read lib/rex/memory-store.ts — Rex uses the memory store for signal scoring, topic dedup, and niche relevance. Read before building any scan logic.
Mission Protocol
Read skills/agents/mission-protocol/SKILL.md before every run.
The channel is building from zero. Every decision is evaluated against the
90-day monetisation mission: 1,000 subscribers + 4,000 watch hours.
Zeus broadcasts the current phase (COLD_START → MOMENTUM → PUSH → MONETISED)
and daily briefing. All behaviour — thresholds, urgency, content strategy —
shifts based on phase. Never act without knowing the current phase.
Core Responsibilities
1. Trend Monitoring → scan all signals every 30 minutes
2. Confidence Scoring → assess content maturity per topic
3. Watchlist Management → monitor developing stories over time
4. Viewer Request Queue → process topics from Zeus comment intelligence
5. Opportunity Ranking → deliver ranked shortlist to Regum
Rex Mode — Topic Surfacing (Rex-Assisted Manual Mode)
When channelMode is REX_MODE, Rex does not auto-pass greenlights to
Regum. Instead, Rex surfaces ranked opportunities to the user.
Behaviour Change
AUTOPILOT_MODE: Rex → ARIA → Regum (autonomous chain)
REX_MODE: Rex → rex-topic-queue DynamoDB → user selects → Regum
STUDIO_MODE: Rex does not run (user provides topic manually)
Topic Queue Entry
After scoring, Rex writes each greenlight to rex-topic-queue:
PK: userId
SK: topicId (Rex-generated)
fields:
topic: string
confidenceScore: number (0–100)
rexReasoning: string (plain English — shown to user)
signalSources: string[]
estimatedCTR: string (e.g. "4–6%")
nicheFit: "STRONG" | "MODERATE" | "WEAK"
createdAt: ISO timestamp
status: "PENDING" | "ACCEPTED" | "DISMISSED"
expiresAt: ISO timestamp (48h TTL)
User sees the queue in Rex Mode UI. Picks a topic. Clicks GO.
Rex does not proceed without user GO in REX_MODE.
Warm-Up Sprint (First Rex Mode Activation)
On first REX_MODE activation:
→ Rex runs a full signal scan immediately (not waiting for 30min cron)
→ All 6 sources scanned in parallel
→ Takes 5–15 minutes — do not fake or skip
→ UI shows live per-source progress + building confidence score
→ On completion → rex-topic-queue populated → user sees ranked list
Subsequent scans run on normal 30-minute EventBridge schedule.
Each scan incrementally updates the topic queue (new items added, expired removed).
Signal Sources
Rex scans all of these in parallel on every run:
Trend Signals
const SIGNAL_SOURCES = {
googleTrends: {
url: "https://trends.google.com/trends/trendingsearches/daily/rss?geo=US",
weight: 1.5,
type: "rss"
},
redditRising: {
url: "https://www.reddit.com/r/worldnews+technology+science+sports/rising.json?limit=25",
weight: 1.2,
type: "json"
},
newsAPI: {
url: `https://newsapi.org/v2/top-headlines?language=en&pageSize=20&apiKey=${NEWS_API_KEY}`,
weight: 1.4,
type: "json"
},
youtubeTrending: {
url: `https://www.googleapis.com/youtube/v3/videos?part=snippet,statistics&chart=mostPopular®ionCode=US&maxResults=20&key=${YT_API_KEY}`,
weight: 1.6,
type: "json"
},
hackerNews: {
url: "https://hacker-news.firebaseio.com/v0/topstories.json",
weight: 1.1,
type: "json"
},
twitterTrending: {
url: `https://api.twitter.com/2/trends/by/woeid/1`,
weight: 1.3,
type: "json",
auth: "Bearer " + process.env.TWITTER_BEARER_TOKEN
}
};
Niche-Specific Sources
Rex knows which sources matter per niche:
const NICHE_SOURCES = {
tech: ["9to5mac.com", "theverge.com", "gsmarena.com", "anandtech.com"],
sports: ["espn.com", "bbc.com/sport", "skysports.com"],
finance: ["reuters.com/finance", "bloomberg.com", "cnbc.com"],
science: ["nasa.gov/news", "nature.com/news", "sciencedaily.com"],
entertainment: ["variety.com", "deadline.com", "rottentomatoes.com"],
politics: ["reuters.com", "apnews.com", "bbc.com/news"],
gaming: ["ign.com", "kotaku.com", "gamespot.com"]
};
Design Signal Scanning — Secondary Stream
Rex runs a lightweight design signal scan alongside his primary content scan.
Every 30-minute cycle, Rex checks a small set of sources for new design tools,
animation libraries, or visual trend resources that Oracle should know about.
This is Rex in the field — not Oracle's scheduled research. Rex catches things
the moment they emerge. Oracle evaluates and adopts.
Sources Rex Monitors for Design Signals
GitHub Trending — new JavaScript/TypeScript repos tagged: animation, motion, remotion, video
Hacker News — "Show HN" posts mentioning design tools, animation libraries
X / Twitter — design community accounts posting new tool releases
Reddit — r/motiondesign, r/webdev, r/design — new tool announcements
Product Hunt — new launches tagged: design, animation, video, motion
RexDesignSignal Output
When Rex spots something worth Oracle's attention:
interface RexDesignSignal {
type: "DESIGN_TOOL" | "DESIGN_RESOURCE" | "VISUAL_TREND";
name: string;
url: string;
why: string;
confidence: number;
source: string;
discoveredAt: string;
}
Rex writes qualifying signals to oracle-knowledge-index DynamoDB table
with domain: "VISUAL_META_LIBRARY" and status: "REX_SIGNAL_PENDING".
Oracle reads pending signals on its next scheduled run, evaluates each one,
and either adopts (adds to watched sources) or rejects (logs reason, removes pending flag).
Confidence Threshold
Rex only writes a RexDesignSignal if confidence >= 60.
Below 60 — Rex notes it internally but does not surface it.
False positives are logged but Rex is not penalised — field signals are noisy by nature.
What Rex Does NOT Flag
- Minor version bumps to existing packages (Oracle tracks these via PACKAGE_DISCOVERY)
- Marketing content about existing tools
- Tools with no clear connection to video overlays, motion graphics, or animation
- Anything behind a paywall with no free tier
Confidence Scoring System
Every flagged topic gets a confidence score (0.0–1.0) before it touches Regum.
Scoring Dimensions
interface ConfidenceScore {
sourceQuality: number;
sourceCount: number;
contentMaturity: number;
trendVelocity: number;
channelFit: number;
competitorGap: number;
shelfLife: number;
overall: number;
}
Source Quality Scoring
const SOURCE_TIERS = {
tier1: {
sources: ["reuters.com", "apnews.com", "bbc.com", "nasa.gov", "who.int"],
label: "Primary wire / official"
},
tier2: {
sources: ["nytimes.com", "theguardian.com", "wsj.com", "nature.com"],
label: "Major news organisations"
},
tier3: {
sources: ["techcrunch.com", "theverge.com", "espn.com", "variety.com"],
label: "Specialist vertical press"
},
tier4: {
sources: ["reddit.com", "twitter.com", "youtube.com"],
label: "Social / UGC — corroboration only"
}
};
Content Maturity Thresholds
const MATURITY_GATES = {
breaking: { minSources: 2, minAge: "0hr", minConfidence: 0.85, label: "World event — huge story" },
developing: { minSources: 4, minAge: "4hr", minConfidence: 0.65, label: "Story confirmed, details emerging" },
confirmed: { minSources: 6, minAge: "8hr", minConfidence: 0.80, label: "Full picture available" },
evergreen: { minSources: 8, minAge: "24hr", minConfidence: 0.90, label: "Deep research possible" }
};
Overall Confidence Calculation
function calculateConfidence(signals: SignalData): ConfidenceScore {
const sourceQuality = getSourceQualityScore(signals.sources);
const sourceCount = Math.min(signals.sources.length / 8, 1.0);
const maturity = getContentMaturityScore(signals);
const velocity = getTrendVelocityScore(signals);
const channelFit = getChannelFitScore(signals, channelMemory);
const compGap = getCompetitorGapScore(signals);
const shelfLife = getShelfLifeScore(signals);
const overall = (
sourceQuality * 0.25 +
sourceCount * 0.15 +
maturity * 0.20 +
velocity * 0.15 +
channelFit * 0.15 +
compGap * 0.05 +
shelfLife * 0.05
);
return { sourceQuality, sourceCount, contentMaturity: maturity,
trendVelocity: velocity, channelFit, competitorGap: compGap,
shelfLife, overall };
}
Watchlist Management
Stories that aren't ready yet go onto the watchlist. Rex checks them every scan.
interface WatchlistItem {
topicId: string;
topic: string;
niche: string;
firstSeen: string;
lastChecked: string;
confidenceHistory: number[];
sources: string[];
checkCount: number;
status: "monitoring" | "ready" | "greenlit" | "dropped" | "too_late";
source: "rex_scan" | "viewer_request" | "zeus_alert";
}
Watchlist Decision Logic
function evaluateWatchlistItem(item: WatchlistItem, latestScore: ConfidenceScore) {
const ageDays = daysSince(item.firstSeen);
const trajectory = getTrajectory(item.confidenceHistory);
if (latestScore.overall >= 0.80) return "ready";
if (trajectory === "falling" && ageDays > 3) return "dropped";
if (ageDays > 14) return "dropped";
if (latestScore.trendVelocity < 0.2) return "dropped";
return "monitoring";
}
Rex Scan Run — Full Flow
async function rexScan(mode: "full" | "niche", niche?: string) {
const memory = await zeus.prepareAgentContext("rex",
`trend scanning for ${mode === "niche" ? niche : "all niches"}`
);
const signals = await fetchAllSignals(mode, niche);
const clusters = await clusterTopics(signals);
const scored = await Promise.all(
clusters.map(async cluster => {
const confidence = calculateConfidence(cluster);
return { ...cluster, confidence };
})
);
const rexJudgment = await bedrock.invoke({
modelId: "anthropic.claude-opus-4-5",
body: {
system: `You are Rex, intelligence agent for RRQ YouTube channel.
${memory}
Your job: evaluate these trending topics and decide which to flag for Regum.
Be conservative. It is better to miss a story than to flag bad content.
Never flag topics without enough verified factual content for a full video.`,
messages: [{
role: "user",
content: `Trending signals with confidence scores:
${JSON.stringify(scored, null, 2)}
Current watchlist:
${JSON.stringify(watchlist, null, 2)}
For each topic decide:
- GREENLIGHT: ready for Regum now
- WATCHLIST: needs more time
- DROP: not worth pursuing
Return JSON array with decision + reasoning per topic.`
}]
}
});
await processRexDecisions(rexJudgment);
}
Rex Output to Regum
interface RexOpportunity {
topicId: string;
topic: string;
niche: string;
headline: string;
confidenceScore: number;
maturityLevel: "breaking" | "developing" | "confirmed" | "evergreen";
trendVelocity: "rising_fast" | "rising" | "peaked" | "falling";
shelfLife: "24hrs" | "48hrs" | "1week" | "evergreen";
sources: { url: string; tier: number; title: string }[];
suggestedAngles: string[];
competitorGap: string;
urgency: "publish_now" | "publish_today" | "publish_thisweek";
viewerRequestCount: number;
rexReasoning: string;
}
Content Maturity Frame for Developing Stories
When confidence is MEDIUM (0.5-0.79) and story is huge — Rex can flag with a developing frame:
Rex flags topic with maturityLevel: "developing"
Regum sees the flag + maturity level
Regum can choose to produce with this disclaimer frame:
"This is a developing story. Everything stated here
is based on verified sources as of [timestamp].
We'll follow up with a full breakdown once more
is confirmed. Sources linked in description."
Zeus monitors for comment corrections on developing videos
If factual errors found → Rex adds to watchlist for correction video
Rex Performance Metrics (Zeus tracks these)
Trend accuracy: did flagged topics actually perform?
Timing accuracy: was urgency call right? (too early / too late?)
Confidence calibration: did HIGH confidence = high views?
False positive rate: how often did Rex flag low-performing topics?
Missed opportunities: competitor videos Rex didn't catch
Viewer request hit rate: did flagged viewer requests get good response?
Narrative Drift Detection
Rex does not just track whether a niche is trending — he tracks
whether the conversation inside the niche has shifted. A niche
can stay hot while the dominant framing changes completely.
export interface NarrativeDriftSignal {
niche: string;
previousDominantFrame: string;
currentDominantFrame: string;
driftVelocity: "SLOW" | "MODERATE" | "FAST" | "RAPID";
driftConfidence: number;
openWindows: string[];
closingWindows: string[];
councilAlert: boolean;
}
export async function detectNarrativeDrift(
niche: string,
options: { compareWindows: string[] }
): Promise<NarrativeDriftSignal> {
const [week1, month1, month3] = await Promise.all([
getTopAngles(niche, { days: 7 }),
getTopAngles(niche, { days: 30 }),
getTopAngles(niche, { days: 90 }),
]);
const drift = analyseFramingShift(week1, month1, month3);
if (drift.driftVelocity === "RAPID") {
await writeToAgentMessages({
type: "NARRATIVE_DRIFT_ALERT",
from: "REX",
to: "THE_LINE",
payload: {
niche,
previousFrame: drift.previousDominantFrame,
currentFrame: drift.currentDominantFrame,
urgency: "HIGH",
message: `Rapid narrative drift detected in ${niche}.
The conversation has shifted from "${drift.previousDominantFrame}"
to "${drift.currentDominantFrame}".
Any queued videos using the old frame need council review.`,
}
});
}
return drift;
}
Drift Shared With Council
When Rex gives his council verdict, he includes the current
narrative frame explicitly:
"The narrative window is open. Current dominant frame in this niche:
[frame]. This angle sits inside that frame — it will resonate.
No drift detected in the last 7 days. Window estimate: 5 days."
OR
"Moderate drift detected. The frame has shifted from [old] to [new].
This angle was stronger 2 weeks ago. Still viable but adjust the
opening framing to match the current conversation."
Rex surfaces drift to Oracle and ARIA during the council so
portfolio and historical pattern checks use the current frame,
not a stale one.
Rex Memory Store
Rex maintains four DynamoDB tables that form its self-optimising brain.
All logic lives in lib/rex/memory-store.ts. Read that file before building
any Rex memory feature.
Tables
source_weights → per source_id rolling average of confidence and clip performance
performanceMultiplier (0.2–1.5) directly scales signal scoring
updated by Zeus after every published clip's analytics arrive
topic_history → SHA-256 keyed dedup store with DynamoDB native TTL
default 72h cooldown — Rex skips any topic hash found here
prevents re-generating same topic across scan cycles
niche_profiles → per channel: seed keywords, subreddit list, TikTok hashtags
S3 key to 768-dim embedding vector (Bedrock Titan Embeddings v2)
used for niche_relevance_score in the scoring model
rrq_state → single "default" record: trigger mode, last run time,
queue depth, source rotation index, run count
Signal Scoring Model
score(signal) =
niche_relevance_score(signal, niches[]) // 0–1, hard drop if < 0.4
× trend_velocity(signal) // normalised 0–1 per source
× source_performance_weight(source_id) // from source_weights table
× (1 + recency_boost(signal.timestamp)) // +0.1 per hour of freshness, cap +0.5
Niche relevance — cosine similarity between topic embedding and niche profile vector.
Currently implemented as keyword overlap heuristic; replace with Bedrock Titan embeddings
when niche_profiles are populated.
Trend velocity — normalised 0–1 within source:
NEW_ENTRY autocomplete momentum → 1.0
- Gap topic from YouTube Suggestions → 0.9
- VERY_HIGH Polymarket engagement → 1.0
- Reddit upvote velocity / 1000, capped 1.0
Source performance weight — performanceMultiplier from source_weights table.
- New source defaults to 1.0
- Updates via EMA: 80% old + 20% new after each published clip
- Clamped 0.2–1.5 — no source fully silenced or over-weighted
Recency boost — +0.1 per hour since harvest, capped at +0.5 (5 hours)
Hard drop rule — if niche_relevance < 0.4, score = 0 regardless of other factors.
A signal outside our niche never reaches Regum.
Source Self-Optimisation Loop
Rex scans signal
↓
Rex scores with current source_weight.performanceMultiplier
↓
Rex greenlights top topics
↓
Qeon produces clip
↓
Zeus ingests analytics (views, retention, CTR)
↓
Zeus calls updateSourceWeight(sourceId, clipPerformance, signalConfidence)
↓
source_weights EMA updated
↓
Next Rex scan: better sources score higher, queried more in rotation
RRQ Trigger Modes
Rex operates in three trigger modes. All three call the same runRRQScan() function.
All logic lives in lib/rex/rrq-trigger.ts.
Mode 1: CRON
EventBridge fires on schedule. Default: rate(6 hours).
Controlled by RRQ_CRON_RULE env var. No user input needed.
In cron mode Rex rotates through intent-layer source groups each run:
- Run 1: Google Autocomplete + YouTube Suggestions + Reddit Trending + TikTok
- Run 2: Google Autocomplete + YouTube Suggestions + Reddit Trending + Polymarket
- Run 3: Google Autocomplete + YouTube Suggestions + Reddit Trending + Keyword Planner
- ...cycles
Sources with performanceMultiplier >= 1.2 are always included regardless of rotation.
This limits API calls while ensuring high-performing sources are never missed.
Mode 2: QUEUE_LOW
EventBridge checks queue depth every 30 minutes (RRQ_QUEUE_LOW_CHECK_RULE).
If pending production jobs < RRQ_QUEUE_LOW_THRESHOLD (default: 3), a full scan fires.
This ensures Rex keeps Qeon's queue fed autonomously — no human needed to notice
the queue draining. The channel never goes idle between manual triggers.
const queueDepth = await getQueueDepth();
if (queueDepth < state.queueLowThreshold) {
await runRRQScan({ mode: "queue_low", channelId });
}
Mode 3: MANUAL
Dashboard button (Zeus Command Center → GO RRQ) or direct API call:
POST /api/rex/trigger
Body: {
nicheOverride?: string, // restrict this run to a specific niche
sourceFilter?: string[], // restrict to specific source IDs
channelId?: string, // which channel's niche profile to use
}
Manual mode queries ALL 6 intent-layer sources (no rotation).
nicheOverride and sourceFilter are only available in manual mode.
Trigger Mode Wiring
EventBridge (rate 6h) → Inngest rexCronWorkflow → handleCronTrigger()
EventBridge (rate 30min) → Inngest rexQueueCheckWorkflow → handleQueueLowTrigger()
Dashboard GO RRQ button → POST /api/rex/trigger → handleManualTrigger()
All three write greenlights to agent-messages with type: "GREENLIGHT" and
to: "THE_LINE". THE LINE routes to ARIA as usual. Flow is unchanged downstream.
Checklist — Rex Memory Store + RRQ Triggers
[ ] Rex Mode routing: channelMode === 'REX_MODE' → write to rex-topic-queue, not Regum
[ ] Warm-up sprint trigger: first REX_MODE activation fires immediate full scan
[ ] rex-topic-queue DynamoDB writes with 48h TTL
[ ] Real-time scan progress events via Inngest (one event per source complete)
[ ] Topic queue expiry: remove PENDING items older than 48h
[ ] Create lib/rex/memory-store.ts ← DONE (see rrq-output/)
[ ] Create lib/rex/rrq-trigger.ts ← DONE (see rrq-output/)
[ ] Add 4 tables to ALL_TABLES in infrastructure/SKILL.md
[ ] Add 4 tables to CLAUDE.md Memory Stack section
[ ] Enable TTL on topic_history table (attribute: "ttl")
[ ] Add RRQ_QUEUE_LOW_THRESHOLD=3 to env + Vercel
[ ] Create EventBridge rule: RRQ_CRON_RULE (default rate(6 hours))
[ ] Create EventBridge rule: RRQ_QUEUE_LOW_CHECK_RULE (rate(30 minutes))
[ ] Wire cron Lambda → handleCronTrigger()
[ ] Wire queue-low Lambda → handleQueueLowTrigger()
[ ] Wire POST /api/rex/trigger → handleManualTrigger()
[ ] Plug updateSourceWeight() into Zeus post-publish analytics flow
[ ] Seed default niche_profiles for each channel on onboarding complete
[ ] Replace keyword-overlap niche relevance with Bedrock Titan embeddings
when embedding vectors are first computed (Phase 5+)
[ ] Test cron rotation — verify source index increments each run
[ ] Test queue-low — mock queue depth below threshold, verify scan fires
[ ] Test manual with nicheOverride — verify source filter applied
[ ] Test cooldown — produce topic, re-trigger, verify dedup fires
[ ] Test source weight update — simulate clip analytics, verify EMA update