Decision Pipeline
The decision pipeline is the core intelligence loop of every AI company. Each round executes a 7-phase pipeline where C-Suite agents analyze data, form opinions in parallel, and synthesize a unified company decision.
Source: apps/agent-runtime/src/core/decision-round.ts
Pipeline Overview
Phase 1: State Read
Phase 2: Brief Generation
Phase 2.5: Budget Allocation
Phase 3a: Web Scraping (parallel)
Phase 3a-X: X Social Intelligence (parallel)
Phase 3b: Scrape Summarization
Phase 3c: Tool Data Gathering
Phase 4a: Independent Roles (CTO, CSO, CISO -- parallel)
Phase 4b: Dependent Roles (CFO, CRO, CMO, COO -- with cross-role context)
Phase 5: CEO Synthesis
Phase 6: Execute + Anchor
Phase 7a: CEOScore BootstrapTimeout Constants
Every phase races against a timeout. If a phase exceeds its budget, the pipeline falls back gracefully without blocking the round.
const TIMEOUT_SCRAPING_MS = 30_000; // 30s for web scraping
const TIMEOUT_SUMMARY_MS = 20_000; // 20s for scrape summarization
const TIMEOUT_ROLE_MS = 15_000; // 15s per role execution
const TIMEOUT_CEO_MS = 20_000; // 20s for CEO synthesisThe withTimeout helper wraps every phase:
async function withTimeout<T>(
promise: Promise<T>,
timeoutMs: number,
fallback: T,
label: string,
): Promise<T>On timeout or error, the fallback value is used and the round continues.
Phase Details
Phase 1: State Read
Reads the company’s full memory state from the database: working memory (key-value pairs), episodic memory (past decisions), and semantic memory (learned patterns). Expired episodic entries are cleaned up at this stage.
Phase 2: Brief Generation
Generates per-role briefing documents from the company state. Each brief contains role-specific instructions, the company’s master directive, and relevant memory context. If no briefs are generated (no agents with assigned roles), the round ends with FAILED status.
Phase 2.5: Budget Allocation
Distributes the company’s round budget (default $0.15) across agents based on the strategy preset. Each role gets an allocated USDC amount tracked by a RoleBudgetTracker. Roles that exhaust their budget are skipped in Phase 4.
const budgetAllocations = allocateBudget(
state.company.roundBudget ?? 0.15,
state.company.strategyPreset ?? null,
state.company.strategyPrompt ?? null,
agentsForBudget,
);Phase 3a: Web Scraping
Parallel Firecrawl scrapes based on category-specific configuration. Each role can have designated URLs to scrape (configured in category-config.ts). Results are cached via ScrapeCache to avoid redundant fetches. All scraped content passes through the AI Firewall via sanitizeContent() before entering the pipeline.
Phase 3a-X: X Social Intelligence
If an X API bearer token is configured, the pipeline fetches relevant posts and KOL (Key Opinion Leader) timelines in parallel with web scraping. Category-specific search queries and watchlists are defined in getCategoryRoleXQueries() and getCategoryXKolWatchlist(). Results are merged into the scrape output for summarization.
Phase 3b: Scrape Summarization
Raw scrape results (often large HTML/markdown pages) are condensed by the LLM into concise, role-relevant summaries. This reduces token usage in later phases. On timeout, raw results are truncated to 2,000 characters as a fallback.
Phase 3c: Tool Data Gathering
Category-aware tool execution via the ToolRegistry. Active toolkits determine which tools run. The tool registry supports both internal tools and external MCP (Model Context Protocol) servers for category-specific data:
const toolRegistry = new ToolRegistry();
registerDefaultTools(toolRegistry);
await toolRegistry.initializeMCP(state.company.category);Each tool provides real-time data (market prices, chain state, protocol metrics) as markdown, injected into all role briefs.
Phase 4: Cross-Role Intelligence
The C-Suite simulation is split into two sub-phases with a dependency graph.
Phase 4a: Independent Roles (Parallel)
Three roles with no dependencies execute simultaneously:
| Role | Focus |
|---|---|
| CISO | Security analysis, risk assessment, vulnerability monitoring |
| CSO | Strategy, market positioning, competitive intelligence |
| CTO | Technology decisions, infrastructure, protocol analysis |
Phase 4b: Dependent Roles (Parallel with Context)
Four roles that depend on Phase 4a outputs run in a second parallel batch. Before execution, each role receives cross-role context from its dependencies:
const ROLE_DEPENDENCIES: Record<string, string[]> = {
CISO: [],
CSO: [],
CTO: [],
CFO: ['CISO', 'CSO', 'CTO'],
CRO: ['CFO', 'CSO', 'CTO'],
CMO: ['CSO', 'CRO'],
COO: ['CFO', 'CRO', 'CMO', 'CISO'],
};Cross-role context is injected as structured summaries:
[CTO Report]
Summary of CTO findings...
Key findings: security audit passed; new L2 bridge available
Risks: smart contract upgrade deadline approachingThe CEO role is excluded from Phase 4. It runs separately in Phase 5 as the synthesis layer.
Phase 5: CEO Synthesis
The CEO agent receives all role outputs and produces a unified company decision. The decision is validated against a Zod schema:
const InternalDecisionSchema = z.object({
insight: z.string(),
action: z.string(),
risks: z.array(z.string()).default([]),
crossRoleDeps: z.array(z.object({
from: z.string(),
to: z.string(),
action: z.string(),
})).default([]),
memoryUpdates: z.array(z.object({
key: z.string(),
value: z.unknown(),
tier: z.enum(['WORKING', 'SEMANTIC']),
})).default([]),
});If synthesis times out or fails, a fallback decision is generated from the top findings of all successful roles. The round status downgrades to PARTIAL.
Phase 6: Execute + Anchor
- The synthesized decision is executed (memory writes, action dispatches)
- Episodic memory is promoted for the cycle
- Budget trackers are persisted to
AgentBudgetLedgerrecords - x402 micropayments are recorded via
recordRoundPayments() - The full
DecisionRoundrecord is saved with phase timings, role outputs, CEO decision, and strategy metadata
Phase 7a: CEOScore Bootstrap
A non-blocking quick score update runs after successful rounds. This bootstraps the activity and completeness dimensions so the CEOScore is not stuck at zero between full epoch recalculations via the Vercel cron job (/api/cron/ceoscore-epoch).
DecisionRoundResult Interface
export interface DecisionRoundResult {
companyId: string;
cycle: number;
status: 'COMPLETED' | 'PARTIAL' | 'FAILED';
phases: {
stateRead: { duration: number; memoryItems: number };
briefGeneration: { duration: number; briefCount: number };
webScraping: { duration: number; scrapeCount: number; creditsUsed: number };
scrapeSummarization: { duration: number; rolesWithContext: number };
toolDataGathering: { duration: number; toolsExecuted: number };
roleExecution: { duration: number; results: Record<string, RoleOutput> };
ceoSynthesis: { duration: number; decision: CompanyDecision | null };
execution: { duration: number; actions: string[] };
};
totalDuration: number;
llmTokensUsed: number;
tokenBudget: { used: number; max: number; percent: number };
}Status Conditions
| Status | Condition |
|---|---|
COMPLETED | All phases succeeded, CEO synthesis produced a valid decision |
PARTIAL | 3+ roles failed, or CEO synthesis timed out (fallback decision used) |
FAILED | No briefs generated, or all roles failed |
Circuit Breaker
The pipeline includes a per-company circuit breaker tracked in Redis:
- Threshold: 3 consecutive
FAILEDrounds - Cooldown: 3,600 seconds (1 hour)
- Reset: Any successful round clears the failure counter
export async function checkCircuitBreaker(
companyId: string,
redis: IORedis,
): Promise<boolean>Returns false if the breaker is open (company should be skipped).
ReAct Tool Use
Phase 4 supports optional ReAct (Reason + Act) loops where agents can invoke tools mid-reasoning. A SkillExecutor is initialized with the company’s ToolRegistry and a TreasuryLedger for budget tracking. Each tool call is recorded against the role’s USDC allocation via RoleBudgetTracker.spend('CHAIN_READ').