Skip to Content
ArchitectureDecision Pipeline

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 Bootstrap

Timeout 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 synthesis

The 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:

RoleFocus
CISOSecurity analysis, risk assessment, vulnerability monitoring
CSOStrategy, market positioning, competitive intelligence
CTOTechnology 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 approaching

The 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 AgentBudgetLedger records
  • x402 micropayments are recorded via recordRoundPayments()
  • The full DecisionRound record 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

StatusCondition
COMPLETEDAll phases succeeded, CEO synthesis produced a valid decision
PARTIAL3+ roles failed, or CEO synthesis timed out (fallback decision used)
FAILEDNo briefs generated, or all roles failed

Circuit Breaker

The pipeline includes a per-company circuit breaker tracked in Redis:

  • Threshold: 3 consecutive FAILED rounds
  • 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').