Episode Schema
The Data Model Behind GESA
The episode is GESA's atomic unit of memory. Every schema decision below follows from the principles of episodic memory: situatedness, immutability, and temporal context.
Core TypeScript Interfaces
Episode
interface Episode {
id: string // Unique identifier (UUID)
timestamp: DateTime // When this episode occurred
context: EpisodeContext // What was the situation?
action: EpisodeAction // What was done?
outcome: EpisodeOutcome | null // What happened? (null until observed)
driftBefore: number // Gap before action
driftAfter: number | null // Gap after action (populated post-outcome)
fetchScore: number // Decision confidence at time of action
temperature: number // Annealing temperature at time of action
tags: string[] // Searchable episode metadata
}EpisodeContext
interface EpisodeContext {
domain: string // 'content' | 'workplace' | 'trading' | 'browser' | 'business_6d' | 'product'
dimension: string // D1–D6 in 6D context, or domain-specific label
driftScore: number // Gap magnitude at time of decision
driftSign: 'positive' | 'negative' | 'zero'
gapVelocity: number // Rate of gap change across recent episodes
fetchScore: number // Action confidence score
chirp: number // Signal strength (0–100)
perch: number // Structural confidence (0–100)
wake: number // Temporal context score (0–100)
tags: string[] // Domain-specific contextual labels
metadata: Record<string, unknown> // Domain-specific fields
}EpisodeAction
interface EpisodeAction {
type: string // The class of intervention (e.g., 'wip_reduction', 'hook_improvement')
description: string // Human-readable description
parameters: Record<string, unknown> // Domain-specific action parameters
source: 'gesa_recommended' | 'manual' | 'automatic'
confidence: number // Confidence at time of action (0–100)
}EpisodeOutcome
interface EpisodeOutcome {
driftAfter: number // Gap after action (remeasured)
gapChange: number // driftBefore - driftAfter (positive = improvement)
timeToResolve: number // Time units until gap closed (domain-defined)
success: boolean // Did the intervention achieve its goal?
notes: string // Optional human annotation
observedAt: DateTime // When outcome was measured
confidence: number // Confidence in the outcome measurement (0–100)
}The Production Schema (StratIQX)
The StratIQX production episode store predates the GESA name. It uses Cloudflare D1 for long-term persistence and KV for the active session buffer.
D1 Table
CREATE TABLE orchestrator_processing_log (
id INTEGER PRIMARY KEY,
tracking_id TEXT NOT NULL, -- Groups episodes by report run
step_name TEXT, -- Human-readable episode label
step_number INTEGER, -- Sequential position in the chain
status TEXT, -- 'started' | 'completed' | 'failed'
duration_ms INTEGER, -- Episode duration
tokens_used INTEGER, -- AI model token consumption
error_message TEXT, -- Failure context (if applicable)
metadata TEXT, -- JSON: model, quality_score, custom_context
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
);KV Buffer
Key: EXECUTION_LOGS:{tracking_id} TTL: 7 days Format: JSON array of episode objects for the current session
The KV buffer provides fast access to the active session's episodes. D1 provides long-term pattern recognition. This is GESA's temporal tiering running in production — hot store for recency, cold store for history.
Immutability Pattern
Episodes must be immutable after capture. The production implementation uses:
// Freeze the episode object at capture time
const episode = Object.freeze({
id: generateId(),
timestamp: new Date(),
context: Object.freeze(contextSnapshot),
action: Object.freeze(actionRecord),
outcome: null, // Not yet observed
// ...
})
// Outcome update creates a NEW object — does not mutate
function withOutcome(episode: Episode, outcome: EpisodeOutcome): Episode {
return Object.freeze({
...episode,
outcome: Object.freeze(outcome),
driftAfter: outcome.driftAfter,
updatedAt: new Date()
})
}The update creates a new immutable object rather than mutating the original. The original capture record is preserved.
Domain-Specific Schema Extensions
Workplace (HEAT)
interface WorkplaceEpisodeContext extends EpisodeContext {
domain: 'workplace'
teamId: string
teamSize: number
sprintNumber: number
sprintPhase: 'early' | 'mid' | 'late'
painStreakDays: number
cognitiveLoad: number // 0–100
contextSwitches: number // per day
busFactorRisk: number // 0–100
}Trading
interface TradingEpisodeContext extends EpisodeContext {
domain: 'trading'
instrument: string
timeframe: string // '1m' | '5m' | '1h' | '4h' | 'D'
volatilityRegime: 'low' | 'medium' | 'high' | 'extreme'
marketSession: string // 'london' | 'ny' | 'asian' | 'overlap'
signalType: string
}6D Business
interface BusinessEpisodeContext extends EpisodeContext {
domain: 'business_6d'
originDimension: string // 'D1' | 'D2' | 'D3' | 'D4' | 'D5' | 'D6'
cascadePath: string[]
cascadeDepth: number
fetchTier: 'EXECUTE' | 'CONFIRM' | 'QUEUE' | 'WAIT'
dimensionScores: Record<string, number>
}Episode Decay Constants
const DECAY_CONSTANTS: Record<string, number> = {
content: 30 * 24 * 60 * 60 * 1000, // 30 days
trading: 30 * 24 * 60 * 60 * 1000, // 30 days
browser: 14 * 24 * 60 * 60 * 1000, // 14 days
product: 90 * 24 * 60 * 60 * 1000, // 90 days
workplace: 180 * 24 * 60 * 60 * 1000, // 180 days
business_6d: 180 * 24 * 60 * 60 * 1000, // 180 days
}