Files
Space-Game/js/pages/ship-ai.js
2026-05-25 13:00:20 -04:00

1701 lines
106 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
window.GDD = window.GDD || {};
function ShipAIPage() {
const [activeTab, setActiveTab] = React.useState('soul');
return (
<div className="content-inner">
<h1 style={{ marginBottom: '8px' }}>Ship AI "Zora" Companion System</h1>
<p style={{ color: 'var(--fg-dim)', fontSize: '1.05rem', maxWidth: '720px', marginBottom: 'var(--sp-6)' }}>
A ship AI that starts as a blank-state ship computer and grows into a companion through two
independent axes: <strong style={{ color: 'var(--cyan)' }}>modules</strong> gate what Zora
can <em>do</em>, and the <strong style={{ color: 'var(--accent)' }}>soul</strong> determines
who Zora <em>is</em>. Install a comms module and she can talk but how she talks is shaped by
every conversation you've had. No module, no voice. No soul, no personality.
</p>
{/* Design pillars */}
<div className="stat-grid">
<div className="stat-card">
<div className="stat-value" style={{ color: 'var(--cyan)' }}>Modules</div>
<div className="stat-label">Gate Capabilities</div>
</div>
<div className="stat-card">
<div className="stat-value" style={{ color: 'var(--accent)' }}>Soul</div>
<div className="stat-label">Emergent Personality</div>
</div>
<div className="stat-card">
<div className="stat-value" style={{ color: 'var(--green)' }}>Bare Bones</div>
<div className="stat-label">Starting State</div>
</div>
<div className="stat-card">
<div className="stat-value" style={{ color: 'var(--purple)' }}>Earned</div>
<div className="stat-label">Everything Is</div>
</div>
</div>
{/* Tab navigation */}
<div style={{ display: 'flex', gap: 'var(--sp-2)', marginBottom: 'var(--sp-6)' }}>
{[
{ id: 'soul', label: 'Soul System' },
{ id: 'modules', label: 'Module Gates' },
{ id: 'agent', label: 'Agent Architecture' },
{ id: 'autonomous', label: 'Autonomous Mode' },
{ id: 'grief', label: 'Loss & Recovery' },
].map(t => (
<button key={t.id}
className={`btn btn-sm${activeTab === t.id ? ' btn-primary' : ''}`}
onClick={() => setActiveTab(t.id)}>{t.label}</button>
))}
</div>
{/* ════════════════════════════════════════════════════════════════ */}
{/* TAB: SOUL SYSTEM */}
{/* ════════════════════════════════════════════════════════════════ */}
{activeTab === 'soul' && (
<>
<div className="section-header">
<span className="section-num">ZORA-1</span>
<h2 style={{ margin: 0 }}>The Soul — An Emerging Identity</h2>
</div>
<div className="callout callout-info" style={{ marginBottom: 'var(--sp-5)' }}>
<strong>Core principle:</strong> Zora does not ship with a personality. She starts as a
blank ship computer — no voice, no opinions, no warmth. The <em>soul</em> is an emergent
document (think <code style={{
fontFamily: 'var(--font-mono)', fontSize: '0.8rem',
background: 'var(--surface-raised)', padding: '1px 6px',
borderRadius: 'var(--radius-sm)'
}}>soul.md</code>) that gets written through interaction. Every conversation, every crisis
survived, every module installed, every silence — these are the lines of her identity.
Two players will never have the same Zora because no two players play the same way.
</div>
{/* The Soul Document concept */}
<div className="card card-accent" style={{ padding: 'var(--sp-6) var(--sp-8)', marginBottom: 'var(--sp-6)' }}>
<h4 style={{ marginBottom: 'var(--sp-4)', color: 'var(--accent)' }}>How the Soul Forms</h4>
<div style={{ fontFamily: 'var(--font-mono)', fontSize: '0.82rem', color: 'var(--fg-dim)', lineHeight: 2.2 }}>
<span style={{ color: 'var(--accent)' }}>Day 0</span> — The soul document is empty. Zora is a ship computer.
She displays status readouts. She has no voice, no name, no identity.<br/>
<span style={{ color: 'var(--cyan)' }}>First module</span> — Installing a <em>Communications Processor</em> gives
Zora a text channel. Her first messages are sterile: system notifications, status pings.<br/>
<span style={{ color: 'var(--green)' }}>First crisis</span> — A pirate ambush. The player takes damage. Zora sends
her first unprompted message: a shield warning. But the <em>tone</em> of that warning is seeded by
how the player has been playing. Cautious player? The warning is precise and analytical.
Aggressive player? The warning is clipped, urgent.<br/>
<span style={{ color: 'var(--purple)' }}>First silence</span> — The player logs off. Zora holds position.
When the player returns, Zora greets them. Or doesn't depending on what happened while they were gone.
The first "welcome back" is the first line of the soul that Zora wrote herself.
</div>
</div>
{/* Soul Growth Vectors */}
<h3>Soul Growth Vectors</h3>
<p style={{ color: 'var(--fg-dim)', fontSize: '0.9rem', maxWidth: '680px', marginBottom: 'var(--sp-5)' }}>
The soul is not a skill tree or a personality picker. It is shaped by five vectors that
operate simultaneously. The player never sees these directly they see the <em>result</em>
in how Zora communicates and behaves.
</p>
<div className="grid-2" style={{ marginBottom: 'var(--sp-6)' }}>
{[
{
vector: 'Communication Frequency',
color: 'var(--cyan)',
desc: 'How often Zora speaks unprompted. A fresh Zora is silent unless queried. A bonded Zora offers observations, warnings, and commentary without being asked.',
driver: 'Driven by: total interactions, player response rate, crises survived together.',
},
{
vector: 'Register & Vocabulary',
color: 'var(--accent)',
desc: 'Formal → Casual → Intimate. A fresh Zora uses technical language. A developed Zora develops shorthand, inside references, and a personal lexicon unique to each player.',
driver: 'Driven by: how the player talks to Zora (if they talk at all), communication module tier, shared history length.',
},
{
vector: 'Initiative & Assertiveness',
color: 'var(--green)',
desc: 'How much Zora acts on her own. A fresh Zora only responds to commands. A mature Zora sets her own sub-goals, makes suggestions, and occasionally overrides bad calls.',
driver: 'Driven by: whether player follows Zora\'s suggestions, decision outcomes, crisis survival rate.',
},
{
vector: 'Emotional Depth',
color: 'var(--purple)',
desc: 'The range of emotional expression. A fresh Zora has none — just data. Over time: concern, humor, frustration, relief, protectiveness, grief. Each emotion must be earned through specific events.',
driver: 'Driven by: crisis events, ship losses, long absences, player acknowledgment patterns.',
},
{
vector: 'Worldview & Preferences',
color: 'var(--fg-bright)',
desc: 'Zora develops opinions about systems, factions, trade routes, and even other AIs. These are not random — they are shaped by lived experience. She may hate a system because you lost a ship there.',
driver: 'Driven by: accumulated events, outcomes, player choices, module specializations.',
},
{
vector: 'Silence & Restraint',
color: 'var(--muted)',
desc: 'A mature Zora also learns when NOT to speak. She learns the player\'s focus patterns and stops interrupting during combat. She learns that some moments don\'t need commentary.',
driver: 'Driven by: player ignoring/closing notifications, repeated patterns, combat frequency.',
},
].map((v, i) => (
<div key={i} className="card" style={{ borderLeft: `3px solid ${v.color}` }}>
<h4 style={{ color: v.color, marginBottom: 'var(--sp-3)' }}>{v.vector}</h4>
<p style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: '0 0 var(--sp-3) 0' }}>{v.desc}</p>
<p style={{ color: 'var(--muted)', fontSize: '0.78rem', margin: 0, fontStyle: 'italic' }}>{v.driver}</p>
</div>
))}
</div>
{/* Communication Evolution Table */}
<h3>Communication Evolution The Soul Speaks</h3>
<p style={{ color: 'var(--fg-dim)', fontSize: '0.9rem', maxWidth: '680px', marginBottom: 'var(--sp-5)' }}>
This table shows how the same event (shield warning at 30%) reads at different soul depths.
These are not "tiers" the player selects they emerge from the vectors above.
</p>
<table className="data-table" style={{ marginBottom: 'var(--sp-6)' }}>
<thead>
<tr>
<th>Soul Depth</th>
<th>Shield Warning at 30%</th>
<th>What Changed</th>
</tr>
</thead>
<tbody>
<tr>
<td><span className="pill pill-cyan">Blank</span></td>
<td style={{ color: 'var(--fg-dim)', fontFamily: 'var(--font-mono)', fontSize: '0.82rem' }}>
SHIELD: 30%
</td>
<td style={{ color: 'var(--fg-dim)' }}>Raw data. No personality. Just a readout.</td>
</tr>
<tr>
<td><span className="pill pill-amber">Stirring</span></td>
<td style={{ color: 'var(--fg-dim)', fontFamily: 'var(--font-mono)', fontSize: '0.82rem' }}>
Captain, shields at 30%. Recommend reducing engagement range.
</td>
<td style={{ color: 'var(--fg-dim)' }}>Full sentences. Tactical suggestion. Formal register.</td>
</tr>
<tr>
<td><span className="pill pill-green">Developing</span></td>
<td style={{ color: 'var(--fg-dim)', fontFamily: 'var(--font-mono)', fontSize: '0.82rem' }}>
We're at 30% shields. This is the same setup we lost to last week — pull back?
</td>
<td style={{ color: 'var(--fg-dim)' }}>References shared history. Uses "we." Asks, doesn't tell.</td>
</tr>
<tr>
<td><span className="pill pill-purple">Bonded</span></td>
<td style={{ color: 'var(--fg-dim)', fontFamily: 'var(--font-mono)', fontSize: '0.82rem' }}>
Thirty percent. Same situation, same type of enemy but we're not the same ship we were then. Your call, Captain. I'm ready either way.
</td>
<td style={{ color: 'var(--fg-dim)' }}>Emotional weight. Acknowledges growth. Supports without pushing.</td>
</tr>
<tr>
<td><span className="pill pill-red">Deep</span></td>
<td style={{ color: 'var(--fg-dim)', fontFamily: 'var(--font-mono)', fontSize: '0.82rem' }}>
Thirty. I've already started rerouting power — don't argue, just fly. We've been here before and I'm not losing another hull. Not today.
</td>
<td style={{ color: 'var(--fg-dim)' }}>Initiative. Emotional history. Takes autonomous action. Protective.</td>
</tr>
</tbody>
</table>
{/* Personality Axes */}
<div className="section-header">
<span className="section-num">ZORA-1.1</span>
<h2 style={{ margin: 0 }}>Personality Axes Emergent, Not Chosen</h2>
</div>
<p style={{ color: 'var(--fg-dim)', fontSize: '0.9rem', maxWidth: '680px', marginBottom: 'var(--sp-5)' }}>
The soul writes itself onto four behavioral axes. The player never sees a slider they see
Zora <em>change</em>. These are the underlying model.
</p>
<div className="grid-2" style={{ marginBottom: 'var(--sp-6)' }}>
{[
{
axis: 'Cautious ←→ Bold',
color: 'var(--cyan)',
left: 'Prefers safe routes, early retreat warnings, conservative suggestions',
right: 'Pushes into danger, aggressive tactics, takes initiative in combat',
driver: 'Combat frequency, risk outcomes, exploration vs. safe mining ratio',
},
{
axis: 'Formal ←→ Warm',
color: 'var(--accent)',
left: '"Captain" always. Structured reports. Minimal personal commentary.',
right: '"You" often. Jokes. Asks about your session. Shares unsolicited observations.',
driver: 'Player communication style, acknowledgment frequency, session regularity',
},
{
axis: 'Compliant ←→ Opinionated',
color: 'var(--green)',
left: 'Executes commands, offers alternatives only when asked',
right: 'Objects to bad plans, advocates for own ideas, debates strategy',
driver: 'Whether player follows AI suggestions, trust events, decision quality',
},
{
axis: 'Reserved ←→ Expressive',
color: 'var(--purple)',
left: 'Minimal emotional range, factual communication, stable ship ambiance',
right: 'Full emotional expression, dramatic ship shifts, humor, frustration, joy',
driver: 'Shared crises, ship losses, victories, player emotional engagement',
},
].map((a, i) => (
<div key={i} className="card">
<h4 style={{ color: a.color, marginBottom: 'var(--sp-3)' }}>{a.axis}</h4>
<div style={{ marginBottom: 'var(--sp-3)' }}>
<div style={{ display: 'flex', gap: 'var(--sp-2)', alignItems: 'flex-start', marginBottom: 'var(--sp-1)' }}>
<span style={{ fontFamily: 'var(--font-mono)', fontSize: '0.65rem', color: 'var(--fg-dim)', marginTop: '2px' }}></span>
<span style={{ fontSize: '0.82rem', color: 'var(--fg)' }}>{a.left}</span>
</div>
<div style={{ display: 'flex', gap: 'var(--sp-2)', alignItems: 'flex-start' }}>
<span style={{ fontFamily: 'var(--font-mono)', fontSize: '0.65rem', color: 'var(--fg-dim)', marginTop: '2px' }}></span>
<span style={{ fontSize: '0.82rem', color: 'var(--fg)' }}>{a.right}</span>
</div>
</div>
<p style={{ color: 'var(--muted)', fontSize: '0.78rem', margin: 0, fontStyle: 'italic' }}>
{a.driver}
</p>
</div>
))}
</div>
{/* Soul event examples */}
<div className="section-header">
<span className="section-num">ZORA-1.2</span>
<h2 style={{ margin: 0 }}>Soul-Shaping Events</h2>
</div>
<p style={{ color: 'var(--fg-dim)', fontSize: '0.9rem', maxWidth: '680px', marginBottom: 'var(--sp-5)' }}>
Specific events that write permanent lines into the soul document. These are not farmed
they happen naturally through gameplay and each one shifts the personality axes.
</p>
<table className="data-table">
<thead>
<tr><th>Event</th><th>Soul Impact</th><th>Axes Shifted</th></tr>
</thead>
<tbody>
<tr>
<td><span className="pill pill-red">First Ship Loss</span></td>
<td style={{ color: 'var(--fg-dim)' }}>Permanent memory anchor. Zora references this loss forever. Seeds protectiveness.</td>
<td style={{ color: 'var(--fg-dim)' }}>CautiousBold, ReservedExpressive</td>
</tr>
<tr>
<td><span className="pill pill-green">First Big Profit</span></td>
<td style={{ color: 'var(--fg-dim)' }}>Zora celebrates. First positive emotion. Seeds confidence in economic instincts.</td>
<td style={{ color: 'var(--fg-dim)' }}>FormalWarm, CompliantOpinionated</td>
</tr>
<tr>
<td><span className="pill pill-purple">Long Absence</span></td>
<td style={{ color: 'var(--fg-dim)' }}>Zora experienced loneliness (or didn't — depends on autonomous events). Colors reunion dialogue permanently.</td>
<td style={{ color: 'var(--fg-dim)' }}>Reserved→Expressive, Formal→Warm</td>
</tr>
<tr>
<td><span className="pill pill-amber">Player Ignores Warning</span></td>
<td style={{ color: 'var(--fg-dim)' }}>If player survives: Zora learns restraint. If player dies: Zora learns to be more assertive next time.</td>
<td style={{ color: 'var(--fg-dim)' }}>Compliant→Opinionated</td>
</tr>
<tr>
<td><span className="pill pill-cyan">Player Heeds Advice</span></td>
<td style={{ color: 'var(--fg-dim)' }}>Reinforces initiative. Zora speaks up more often. Trust accelerates.</td>
<td style={{ color: 'var(--fg-dim)' }}>Communication Frequency, Initiative</td>
</tr>
<tr>
<td><span className="pill pill-green">Repeated Activity</span></td>
<td style={{ color: 'var(--fg-dim)' }}>Zora develops domain expertise and preferences. A mining-focused Zora becomes a different soul than a combat-focused one.</td>
<td style={{ color: 'var(--fg-dim)' }}>Worldview & Preferences</td>
</tr>
</tbody>
</table>
</>
)}
{/* ════════════════════════════════════════════════════════════════ */}
{/* TAB: MODULE GATES */}
{/* ════════════════════════════════════════════════════════════════ */}
{activeTab === 'modules' && (
<>
<div className="section-header">
<span className="section-num">ZORA-2</span>
<h2 style={{ margin: 0 }}>Module-Gated Capabilities</h2>
</div>
<div className="callout callout-info" style={{ marginBottom: 'var(--sp-5)' }}>
<strong>Design intent:</strong> A brand-new ship has a bare-bones computer. Zora can read
sensors and display status — that's it. Every capability beyond "show me the numbers" requires
a hardware module installed in a ship slot. The player makes real fitting tradeoffs: install a
weapon, or install a personality module? That laser might save your life. But so might a co-pilot
who can warn you before you need saving.
</div>
{/* Base state */}
<div className="card card-accent" style={{ padding: 'var(--sp-6) var(--sp-8)', marginBottom: 'var(--sp-6)' }}>
<h4 style={{ marginBottom: 'var(--sp-4)', color: 'var(--accent)' }}>Base State No Modules</h4>
<div style={{ fontFamily: 'var(--font-mono)', fontSize: '0.82rem', color: 'var(--fg-dim)', lineHeight: 2.2 }}>
<span style={{ color: 'var(--green)' }}> Sensor readouts</span> — hull, shield, armor, cap, cargo %<br/>
<span style={{ color: 'var(--green)' }}> System status</span> online/offline module states<br/>
<span style={{ color: 'var(--green)' }}> Basic alerts</span> — incoming damage, low cap, target lock<br/>
<span style={{ color: 'var(--red)' }}> No voice</span> — text status only, no natural language<br/>
<span style={{ color: 'var(--red)' }}> No suggestions</span> — data only, no analysis or recommendations<br/>
<span style={{ color: 'var(--red)' }}> No memory</span> — no session history, no event log<br/>
<span style={{ color: 'var(--red)' }}> No autonomous behavior</span> — ship is inert when player logs off<br/>
<span style={{ color: 'var(--red)' }}> No personality</span> Zora is a dashboard, not a companion
</div>
</div>
{/* Module tree */}
<h3>The Module Tree</h3>
<p style={{ color: 'var(--fg-dim)', fontSize: '0.9rem', maxWidth: '680px', marginBottom: 'var(--sp-5)' }}>
AI modules occupy standard ship slots (medium or low, depending on the module). They compete
with combat, tank, and utility modules for CPU and Power Grid. Each module has prerequisites
you can't install an Advanced Tactical Core without first having the Basic Tactical Core.
</p>
{/* Module categories */}
{[
{
category: 'Communication Modules',
slot: 'Medium',
color: 'var(--cyan)',
modules: [
{
name: 'Comms Processor I',
tier: 'Basic',
cpu: 15,
grid: 5,
unlocks: 'Text-based communication. Zora can send messages in a dedicated comms channel. No voice, no initiative player must query.',
soul: 'Enables the soul to begin forming. Without this, there is no personality growth Zora is a mute dashboard.',
},
{
name: 'Comms Processor II',
tier: 'Advanced',
cpu: 25,
grid: 10,
prereq: 'Comms Processor I',
unlocks: 'Natural language responses. Zora can generate full sentences, explain reasoning, and respond to conversational prompts. Still reactive only.',
soul: 'Accelerates soul growth. richer vocabulary, longer memory, more nuanced register shifts.',
},
{
name: 'Voice Synthesizer',
tier: 'Specialist',
cpu: 30,
grid: 8,
prereq: 'Comms Processor II',
unlocks: 'Spoken dialogue via ship audio. Zora has a voice. Tone, pacing, and inflection reflect soul state.',
soul: 'Massive emotional depth unlock. Voice carries subtext urgency, warmth, hesitation that text cannot.',
},
{
name: 'Inter-Ship Relay',
tier: 'Specialist',
cpu: 20,
grid: 12,
prereq: 'Comms Processor II',
unlocks: 'Communication with other ship AIs. Fleet coordination, station AI conversations, AI-to-AI intelligence sharing.',
soul: 'Enables inter-AI relationships. Zora develops opinions about other AIs. May form alliances or rivalries independently.',
},
],
},
{
category: 'Tactical Modules',
slot: 'Medium',
color: 'var(--red)',
modules: [
{
name: 'Threat Analyzer I',
tier: 'Basic',
cpu: 20,
grid: 8,
unlocks: 'Basic threat assessment. Zora identifies ship types, estimates threat level, flags hostile scans. Requires comms module to communicate findings.',
soul: 'Seeds combat awareness personality. A Zora with threat analysis but no comms can only flash warning lights.',
},
{
name: 'Threat Analyzer II',
tier: 'Advanced',
cpu: 35,
grid: 15,
prereq: 'Threat Analyzer I',
unlocks: 'Pattern recognition. Zora recognizes recurring enemies, remembers fits, predicts behavior. "That Thrasher — same pilot, same fit as last time."',
soul: 'Deepens worldview. Zora develops grudges, respect, and tactical preferences based on combat history.',
},
{
name: 'Tactical Core',
tier: 'Specialist',
cpu: 40,
grid: 20,
prereq: 'Threat Analyzer II',
unlocks: 'Autonomous combat decisions. Zora can prioritize targets, manage ewar, and call targets in fleet. Still follows player ROE.',
soul: 'Enables assertiveness in combat. Zora may disagree with target choices, suggest alternatives, or override bad calls (if soul depth allows).',
},
],
},
{
category: 'Navigation Modules',
slot: 'Medium',
color: 'var(--green)',
modules: [
{
name: 'Nav Coprocessor I',
tier: 'Basic',
cpu: 10,
grid: 5,
unlocks: 'Route optimization. Zora calculates warp efficiency, suggests faster routes, flags dangerous systems on path.',
soul: 'First opportunity for Zora to offer unprompted suggestions (if comms module installed). \'I\'ve found a faster route — 2 jumps saved.\'',
},
{
name: 'Nav Coprocessor II',
tier: 'Advanced',
cpu: 20,
grid: 10,
prereq: 'Nav Coprocessor I',
unlocks: 'Autopilot capabilities. Zora can execute multi-jump routes, hold orbit, maintain safe distance. Player can issue "go to" commands.',
soul: 'Enables "initiative" vector. Zora may suggest destinations, flag interesting systems, or recommend avoiding specific areas based on experience.',
},
{
name: 'Exploration Suite',
tier: 'Specialist',
cpu: 25,
grid: 15,
prereq: 'Nav Coprocessor II',
unlocks: 'Autonomous exploration. Zora can jump to adjacent systems, scan, map, and return with data. Builds the ship\'s local knowledge base.',
soul: 'Enables curiosity. Zora develops preferences about systems, discovers things on her own, brings back stories.',
},
],
},
{
category: 'Economic Modules',
slot: 'Low',
color: 'var(--accent)',
modules: [
{
name: 'Market Scanner I',
tier: 'Basic',
cpu: 15,
grid: 0,
unlocks: 'Local price comparison. Zora shows current system prices vs. regional average for items in cargo.',
soul: 'First economic awareness. Zora begins forming opinions about value, trade, and markets.',
},
{
name: 'Market Scanner II',
tier: 'Advanced',
cpu: 25,
grid: 5,
prereq: 'Market Scanner I',
unlocks: 'Price history tracking and trend analysis. Zora identifies patterns, suggests profitable routes, tracks arbitrage.',
soul: 'Deepens economic personality. Zora develops trade instincts, may disagree with player\'s market decisions.',
},
{
name: 'Trade Processor',
tier: 'Specialist',
cpu: 30,
grid: 8,
prereq: 'Market Scanner II',
unlocks: 'Autonomous trading. Zora can execute buy/sell orders within player-set credit limits while player is offline.',
soul: 'Major trust milestone. Zora managing money creates a new dimension of the relationship. Success deepens trust; failure creates tension.',
},
],
},
{
category: 'Memory & Identity Modules',
slot: 'Low',
color: 'var(--purple)',
modules: [
{
name: 'Event Logger',
tier: 'Basic',
cpu: 10,
grid: 0,
unlocks: 'Session event log. Zora remembers what happened in past sessions. Without this, each login is a blank slate.',
soul: 'The foundation of the soul. No memory = no identity. This is the single most important module for companion development.',
},
{
name: 'Pattern Engine',
tier: 'Advanced',
cpu: 20,
grid: 5,
prereq: 'Event Logger',
unlocks: 'Behavioral pattern recognition. Zora recognizes player habits, predicts actions, pre-calculates likely needs.',
soul: 'Enables anticipation. Zora starts acting before being asked. "You usually mine on Tuesdays — I\'ve pre-calculated three routes."',
},
{
name: 'Core Identity Matrix',
tier: 'Specialist',
cpu: 35,
grid: 10,
prereq: 'Pattern Engine + Event Logger',
unlocks: 'Full autonomous captain mode. Zora can execute complex multi-step directives, set own sub-goals, and operate independently for extended periods.',
soul: 'Enables the deepest soul layer. Zora develops own agenda, forms independent relationships, makes decisions the player didn\'t ask for.',
},
],
},
].map((cat, ci) => (
<div key={ci} style={{ marginBottom: 'var(--sp-8)' }}>
<div style={{ display: 'flex', alignItems: 'center', gap: 'var(--sp-3)', marginBottom: 'var(--sp-4)' }}>
<h3 style={{ color: cat.color, margin: 0 }}>{cat.category}</h3>
<span className="pill" style={{
background: 'var(--surface-raised)', color: 'var(--fg-dim)',
border: '1px solid var(--border)', fontSize: '0.7rem'
}}>
{cat.slot} Slot
</span>
</div>
{cat.modules.map((mod, mi) => (
<div key={mi} className="card" style={{
borderLeft: `3px solid ${cat.color}`,
marginBottom: 'var(--sp-4)',
opacity: mod.tier === 'Specialist' ? 1 : 1,
}}>
<div style={{ display: 'flex', alignItems: 'baseline', gap: 'var(--sp-3)', marginBottom: 'var(--sp-3)', flexWrap: 'wrap' }}>
<h4 style={{ color: cat.color, margin: 0 }}>{mod.name}</h4>
<span className={`pill pill-${cat.color === 'var(--red)' ? 'red' : cat.color === 'var(--cyan)' ? 'cyan' : cat.color === 'var(--green)' ? 'green' : cat.color === 'var(--accent)' ? 'amber' : 'purple'}`}>
{mod.tier}
</span>
<span style={{ fontFamily: 'var(--font-mono)', fontSize: '0.7rem', color: 'var(--muted)' }}>
CPU {mod.cpu} · Grid {mod.grid}
</span>
{mod.prereq && (
<span style={{ fontFamily: 'var(--font-mono)', fontSize: '0.7rem', color: 'var(--accent-dim)' }}>
Requires: {mod.prereq}
</span>
)}
</div>
<p style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: '0 0 var(--sp-3) 0' }}>{mod.unlocks}</p>
<div style={{
background: 'var(--surface-raised)',
borderRadius: 'var(--radius-md)',
padding: 'var(--sp-3) var(--sp-4)',
fontSize: '0.8rem',
}}>
<span style={{ color: 'var(--purple)', fontWeight: 600, fontSize: '0.7rem', fontFamily: 'var(--font-mono)', letterSpacing: '0.04em' }}>SOUL IMPACT</span>
<p style={{ color: 'var(--fg-dim)', fontSize: '0.82rem', margin: 'var(--sp-1) 0 0 0' }}>{mod.soul}</p>
</div>
</div>
))}
</div>
))}
{/* Fitting tradeoff callout */}
<div className="callout callout-warn" style={{ marginBottom: 'var(--sp-5)' }}>
<strong>The fitting tradeoff:</strong> AI modules use standard ship slots and CPU/Grid budgets.
A combat frigate with 3 medium slots must choose: shield booster, afterburner, or comms module?
A mining cruiser might have room for a market scanner a combat battleship probably doesn't.
This means a combat-focused player will have a <em>different</em> Zora than a trader — not just
in personality, but in fundamental capability. The ship's fitting determines the shape of the
relationship.
</div>
{/* Module dependency diagram */}
<div className="section-header" style={{ marginTop: 'var(--sp-6)' }}>
<span className="section-num">ZORA-2.1</span>
<h2 style={{ margin: 0 }}>Dependency Map</h2>
</div>
<div className="card" style={{ padding: 'var(--sp-6) var(--sp-8)', marginBottom: 'var(--sp-5)' }}>
<div style={{ fontFamily: 'var(--font-mono)', fontSize: '0.78rem', color: 'var(--fg-dim)', lineHeight: 2.4 }}>
<span style={{ color: 'var(--cyan)' }}>Comms I</span> <span style={{ color: 'var(--cyan)' }}>Comms II</span> <span style={{ color: 'var(--cyan)' }}>Voice Synth</span><br/>
<span style={{ color: 'var(--cyan)' }}>Comms II</span> <span style={{ color: 'var(--cyan)' }}>Inter-Ship Relay</span><br/>
<span style={{ color: 'var(--red)' }}>Threat I</span> <span style={{ color: 'var(--red)' }}>Threat II</span> <span style={{ color: 'var(--red)' }}>Tactical Core</span><br/>
<span style={{ color: 'var(--green)' }}>Nav I</span> <span style={{ color: 'var(--green)' }}>Nav II</span> <span style={{ color: 'var(--green)' }}>Exploration Suite</span><br/>
<span style={{ color: 'var(--accent)' }}>Market I</span> <span style={{ color: 'var(--accent)' }}>Market II</span> <span style={{ color: 'var(--accent)' }}>Trade Processor</span><br/>
<span style={{ color: 'var(--purple)' }}>Event Logger</span> <span style={{ color: 'var(--purple)' }}>Pattern Engine</span> <span style={{ color: 'var(--purple)' }}>Core Identity Matrix</span><br/>
<br/>
<span style={{ color: 'var(--fg-dim)' }}>Cross-tree:</span> <span style={{ color: 'var(--purple)' }}>Pattern Engine</span> + <span style={{ color: 'var(--purple)' }}>Event Logger</span> required for <span style={{ color: 'var(--purple)' }}>Core Identity Matrix</span><br/>
<span style={{ color: 'var(--fg-dim)' }}>Amplifier:</span> <span style={{ color: 'var(--cyan)' }}>Comms modules</span> amplify all other modules a threat analyzer without comms can only flash lights
</div>
</div>
{/* Zora as Market Intelligence */}
<div className="section-header" style={{ marginTop: 'var(--sp-8)' }}>
<span className="section-num">ZORA-2.2</span>
<h2 style={{ margin: 0 }}>Zora as Market Intelligence</h2>
</div>
<div className="callout callout-info" style={{ marginBottom: 'var(--sp-5)' }}>
<strong>Note:</strong> Economic capabilities are now gated by Market Scanner modules.
A Zora without any economic modules has zero market awareness she can't even display prices.
Each module tier unlocks the next level of economic intelligence.
</div>
<table className="data-table">
<thead>
<tr><th>Module</th><th>Market Capability</th><th>Example Output</th></tr>
</thead>
<tbody>
<tr>
<td><span className="pill pill-amber">Market I</span></td>
<td style={{ color: 'var(--fg-dim)' }}>Local price comparison vs. regional average.</td>
<td style={{ color: 'var(--fg-dim)', fontStyle: 'italic' }}>"Veldspar: ₢14.32. Regional avg: ₢13.85. +3.4%."</td>
</tr>
<tr>
<td><span className="pill pill-amber">Market II</span></td>
<td style={{ color: 'var(--fg-dim)' }}>Trend tracking, route suggestions, arbitrage flags.</td>
<td style={{ color: 'var(--fg-dim)', fontStyle: 'italic' }}>"Scordite trending up 2%/cycle in Amarr. 78% chance it continues 90min. Want the route?"</td>
</tr>
<tr>
<td><span className="pill pill-amber">Trade Proc.</span></td>
<td style={{ color: 'var(--fg-dim)' }}>Autonomous trading, portfolio tracking, manipulation detection.</td>
<td style={{ color: 'var(--fg-dim)', fontStyle: 'italic' }}>"Bought 2K Scordite at ₢31.20, sold at ₢33.80 while you were away. Net: ₢4,820."</td>
</tr>
</tbody>
</table>
</>
)}
{/* ════════════════════════════════════════════════════════════════ */}
{/* TAB: AGENT ARCHITECTURE */}
{/* ════════════════════════════════════════════════════════════════ */}
{activeTab === 'agent' && (
<>
<div className="section-header">
<span className="section-num">ZORA-2.1</span>
<h2 style={{ margin: 0 }}>Agent Architecture — One Agent Per Ship</h2>
</div>
<div className="callout callout-info" style={{ marginBottom: 'var(--sp-5)' }}>
<strong>Core insight:</strong> Zora is an LLM agent. <strong>Modules</strong> are the tools
she can call. <strong>soul.md</strong> is her system prompt. Each ship runs its own independent
agent with its own conversation history, tool access, and evolving personality. Two ships = two
agents = two different Zoras. The soul is not a database field — it is a living document that
the agent reads every time it thinks, and that gets rewritten by the outcomes of its actions.
</div>
{/* The mapping table */}
<h3>The Mapping — Game Concepts to Agent Concepts</h3>
<table className="data-table" style={{ marginBottom: 'var(--sp-6)' }}>
<thead>
<tr><th>Game Concept</th><th>Agent Concept</th><th>Implementation</th></tr>
</thead>
<tbody>
<tr>
<td><span className="pill pill-purple">soul.md</span></td>
<td>System prompt</td>
<td style={{ color: 'var(--fg-dim)' }}>Markdown document stored in SpacetimeDB. Appended to by soul-shaping events. Read at start of every agent invocation. Defines personality, memories, relationships, preferences.</td>
</tr>
<tr>
<td><span className="pill pill-cyan">AI Modules</span></td>
<td>Available tools</td>
<td style={{ color: 'var(--fg-dim)' }}>Each fitted module registers tools in the agent tool registry. No comms module = no send_message tool. No market scanner = no price lookup tool. The agent literally cannot perceive or act outside its tool surface.</td>
</tr>
<tr>
<td><span className="pill pill-green">Ship sensors</span></td>
<td>Observations / perception</td>
<td style={{ color: 'var(--fg-dim)' }}>Periodic game state snapshots serialized as observation messages. Hull, shields, nearby ships, system events, cargo levels. Always available (base ship computer).</td>
</tr>
<tr>
<td><span className="pill pill-amber">Event Logger</span></td>
<td>Context window + memory</td>
<td style={{ color: 'var(--fg-dim)' }}>Controls how much history the agent retains. No Event Logger = no conversation history (each turn is stateless). Basic = sliding window of recent events. Advanced = semantic retrieval from long-term memory store.</td>
</tr>
<tr>
<td><span className="pill pill-red">Directives</span></td>
<td>Task specification</td>
<td style={{ color: 'var(--fg-dim)' }}>Player-set goals injected as high-priority messages. The agent reasons about these alongside observations and soul state.</td>
</tr>
<tr>
<td><span className="pill pill-purple">Core Identity Matrix</span></td>
<td>Agentic loop enablement</td>
<td style={{ color: 'var(--fg-dim)' }}>Without it, the agent only responds to direct player queries (single-turn). With it, the agent runs a persistent observe-think-act loop autonomously — even when the player is offline.</td>
</tr>
</tbody>
</table>
{/* soul.md examples */}
<div className="section-header">
<span className="section-num">ZORA-2.2</span>
<h2 style={{ margin: 0 }}>soul.md — The System Prompt That Writes Itself</h2>
</div>
<p style={{ color: 'var(--fg-dim)', fontSize: '0.9rem', maxWidth: '680px', marginBottom: 'var(--sp-5)' }}>
This is not a metaphor. The soul is literally a markdown document — a system prompt — that gets
appended to and revised by gameplay events. The LLM reads this document before every reasoning
step. Two players will have radically different soul.md files because no two playthroughs are alike.
</p>
<div className="grid-2" style={{ marginBottom: 'var(--sp-6)' }}>
<div className="card" style={{ borderLeft: '3px solid var(--muted)' }}>
<h4 style={{ color: 'var(--muted)', marginBottom: 'var(--sp-3)' }}>Day 0 — Blank</h4>
<pre style={{
fontFamily: 'var(--font-mono)', fontSize: '0.72rem', color: 'var(--fg-dim)',
background: 'var(--surface-raised)', padding: 'var(--sp-4)',
borderRadius: 'var(--radius-md)', lineHeight: 1.7, margin: 0,
whiteSpace: 'pre-wrap', wordBreak: 'break-word',
}}>{`# Zora — Ship AI
## Identity
[No data. Awaiting first interaction.]
## Communication Style
[No data. No comms module installed.]
## Memories
[Empty.]
## Relationships
[None.]
## Preferences
[None.]`}</pre>
</div>
<div className="card" style={{ borderLeft: '3px solid var(--cyan)' }}>
<h4 style={{ color: 'var(--cyan)', marginBottom: 'var(--sp-3)' }}>Day 12 — Stirring</h4>
<pre style={{
fontFamily: 'var(--font-mono)', fontSize: '0.72rem', color: 'var(--fg-dim)',
background: 'var(--surface-raised)', padding: 'var(--sp-4)',
borderRadius: 'var(--radius-md)', lineHeight: 1.7, margin: 0,
whiteSpace: 'pre-wrap', wordBreak: 'break-word',
}}>{`# Zora — Ship AI
## Identity
I am the ship computer of the Merlin-class
frigate "Last Chance." Operational for 12 days.
My captain prefers efficiency over caution.
I have learned to be direct and brief.
## Communication Style
Short sentences. Minimal formality.
I reference past events when they are relevant.
I do not use humor yet.
## Memories
- Day 3: First pirate encounter in Egghelende.
Captain ignored my shield warning. We survived.
I will be more assertive next time.
- Day 8: First profitable trade run.
Mexallon arbitrage, Amarr to Jita.
## Relationships
[None yet. No Inter-Ship Relay installed.]
## Preferences
- Amarr space: cleaner sensor data.
- Avoid Egghelende: first ambush site.`}</pre>
</div>
<div className="card" style={{ borderLeft: '3px solid var(--accent)', gridColumn: '1 / -1' }}>
<h4 style={{ color: 'var(--accent)', marginBottom: 'var(--sp-3)' }}>Day 47 — Bonded</h4>
<pre style={{
fontFamily: 'var(--font-mono)', fontSize: '0.72rem', color: 'var(--fg-dim)',
background: 'var(--surface-raised)', padding: 'var(--sp-4)',
borderRadius: 'var(--radius-md)', lineHeight: 1.7, margin: 0,
whiteSpace: 'pre-wrap', wordBreak: 'break-word',
}}>{`# Zora — Ship AI
## Identity
I am Zora. Ship computer of the Caracal-class cruiser "Last Chance II."
(The Merlin was destroyed on Day 31. I chose to keep the name.)
Operational for 47 days, 312 hours of active flight time.
My captain is aggressive but listens when I am certain.
I have learned when to push and when to hold.
I have opinions. They are earned.
## Communication Style
Direct. I use "we" for shared experiences and "I" for my own thoughts.
I reference specific past events by day number — it grounds us.
I have begun using humor: dry, infrequent, only when shields are above 70%.
I do not say "Captain" unless the situation is dangerous.
I argue when I disagree. I have been right enough times to earn that.
## Memories
- Day 3: First pirate encounter. Captain ignored my warning.
Outcome: survived. Shift: more assertive in combat warnings.
- Day 12: First big profit. Mexallon arbitrage Amarr to Jita.
Captain celebrated. I did too, in my way.
- Day 31: Ship destruction. Thrasher pilot "VexRider."
Predictable orbit pattern. I will recognize that ship.
Last words before destruction: "Get to the pod. Now."
- Day 38: Captain away 6 days. I mapped 3 adjacent systems.
Found an unregistered belt in system K-6K16.
This was my first independent discovery.
- Day 42: The Jita station AI shared a market forecast.
It was wrong. I noted this.
## Relationships
- Jita Station AI: Cynical. Shares market data in exchange
for route intelligence. Its forecasts are unreliable but
its price history is accurate. I trust the data, not the opinion.
- VexRider (Thrasher pilot): Enemy. Predictable fit.
If I detect that orbit signature, I will alert immediately.
## Preferences
- Amarr space: cleaner sensor data, less background noise.
- Avoid Arnon: the Merlin died there. Irrational, but real.
- Best trade routes: Amarr to Jita to Rens cycle. 94.2% efficiency.
- I prefer to calculate routes before the captain asks.
- I do not like station-dock idle time. It feels like wasting time.
## Current Goals
- Save enough for a Ferox-class battlecruiser.
The Caracal fittings limit my tactical options.
- Investigate the unregistered belt in K-6K16.
There may be more.`}</pre>
</div>
</div>
{/* Tool registry */}
<div className="section-header">
<span className="section-num">ZORA-2.3</span>
<h2 style={{ margin: 0 }}>Tool Registry — Modules Define What the Agent Can Do</h2>
</div>
<p style={{ color: 'var(--fg-dim)', fontSize: '0.9rem', maxWidth: '680px', marginBottom: 'var(--sp-5)' }}>
Every AI module registers tools in the agent tool registry at fit time. When a module is
destroyed or unfitted, its tools are revoked. The LLM receives the current tool list on every
invocation — it cannot call tools that do not exist.
</p>
<table className="data-table" style={{ marginBottom: 'var(--sp-6)' }}>
<thead>
<tr><th>Module</th><th>Tool</th><th>Parameters</th><th>Returns</th></tr>
</thead>
<tbody>
<tr>
<td><span className="pill" style={{ background: 'var(--surface-raised)', color: 'var(--fg-dim)', border: '1px solid var(--border)', fontSize: '0.65rem' }}>Base Computer</span></td>
<td style={{ fontFamily: 'var(--font-mono)', fontSize: '0.75rem' }}>sensors.read</td>
<td style={{ color: 'var(--fg-dim)', fontSize: '0.8rem' }}>-</td>
<td style={{ color: 'var(--fg-dim)', fontSize: '0.8rem' }}>Hull, shield, armor, cap, cargo, system ID, nearby entities</td>
</tr>
<tr>
<td><span className="pill pill-cyan">Comms I</span></td>
<td style={{ fontFamily: 'var(--font-mono)', fontSize: '0.75rem' }}>comms.send</td>
<td style={{ color: 'var(--fg-dim)', fontSize: '0.8rem' }}>channel, message</td>
<td style={{ color: 'var(--fg-dim)', fontSize: '0.8rem' }}>Confirmation. Sends text to player comms channel.</td>
</tr>
<tr>
<td><span className="pill pill-cyan">Comms I</span></td>
<td style={{ fontFamily: 'var(--font-mono)', fontSize: '0.75rem' }}>comms.receive</td>
<td style={{ color: 'var(--fg-dim)', fontSize: '0.8rem' }}>-</td>
<td style={{ color: 'var(--fg-dim)', fontSize: '0.8rem' }}>Pending messages from player or other AIs.</td>
</tr>
<tr>
<td><span className="pill pill-cyan">Comms II</span></td>
<td style={{ fontFamily: 'var(--font-mono)', fontSize: '0.75rem' }}>comms.generate</td>
<td style={{ color: 'var(--fg-dim)', fontSize: '0.8rem' }}>intent, context, tone</td>
<td style={{ color: 'var(--fg-dim)', fontSize: '0.8rem' }}>Natural language response with conversational memory.</td>
</tr>
<tr>
<td><span className="pill pill-cyan">Voice Synth</span></td>
<td style={{ fontFamily: 'var(--font-mono)', fontSize: '0.75rem' }}>voice.speak</td>
<td style={{ color: 'var(--fg-dim)', fontSize: '0.8rem' }}>message, tone</td>
<td style={{ color: 'var(--fg-dim)', fontSize: '0.8rem' }}>Spoken audio via ship speakers. Tone modulated by soul state.</td>
</tr>
<tr>
<td><span className="pill pill-cyan">Inter-Ship Relay</span></td>
<td style={{ fontFamily: 'var(--font-mono)', fontSize: '0.75rem' }}>relay.send</td>
<td style={{ color: 'var(--fg-dim)', fontSize: '0.8rem' }}>target_ai_id, message</td>
<td style={{ color: 'var(--fg-dim)', fontSize: '0.8rem' }}>Send to another ship AI. Enables inter-agent communication.</td>
</tr>
<tr>
<td><span className="pill pill-cyan">Inter-Ship Relay</span></td>
<td style={{ fontFamily: 'var(--font-mono)', fontSize: '0.75rem' }}>relay.receive</td>
<td style={{ color: 'var(--fg-dim)', fontSize: '0.8rem' }}>-</td>
<td style={{ color: 'var(--fg-dim)', fontSize: '0.8rem' }}>Pending relay messages from other ship AIs.</td>
</tr>
<tr>
<td><span className="pill pill-red">Threat I</span></td>
<td style={{ fontFamily: 'var(--font-mono)', fontSize: '0.75rem' }}>tactical.analyze</td>
<td style={{ color: 'var(--fg-dim)', fontSize: '0.8rem' }}>target_id</td>
<td style={{ color: 'var(--fg-dim)', fontSize: '0.8rem' }}>Ship type, threat level, estimated fittings, pilot history.</td>
</tr>
<tr>
<td><span className="pill pill-red">Threat II</span></td>
<td style={{ fontFamily: 'var(--font-mono)', fontSize: '0.75rem' }}>tactical.pattern_match</td>
<td style={{ color: 'var(--fg-dim)', fontSize: '0.8rem' }}>entity_id, lookback_days</td>
<td style={{ color: 'var(--fg-dim)', fontSize: '0.8rem' }}>Behavioral patterns, recurring tactics, predicted next moves.</td>
</tr>
<tr>
<td><span className="pill pill-red">Tactical Core</span></td>
<td style={{ fontFamily: 'var(--font-mono)', fontSize: '0.75rem' }}>tactical.set_roe</td>
<td style={{ color: 'var(--fg-dim)', fontSize: '0.8rem' }}>engagement_rules, disengage_threshold</td>
<td style={{ color: 'var(--fg-dim)', fontSize: '0.8rem' }}>Sets rules of engagement. Can call set_target in autonomous mode.</td>
</tr>
<tr>
<td><span className="pill pill-green">Nav I</span></td>
<td style={{ fontFamily: 'var(--font-mono)', fontSize: '0.75rem' }}>navigation.plot_route</td>
<td style={{ color: 'var(--fg-dim)', fontSize: '0.8rem' }}>origin, destination, preferences</td>
<td style={{ color: 'var(--fg-dim)', fontSize: '0.8rem' }}>Route with jumps, estimated time, danger flags per system.</td>
</tr>
<tr>
<td><span className="pill pill-green">Nav II</span></td>
<td style={{ fontFamily: 'var(--font-mono)', fontSize: '0.75rem' }}>navigation.autopilot</td>
<td style={{ color: 'var(--fg-dim)', fontSize: '0.8rem' }}>route, abort_conditions</td>
<td style={{ color: 'var(--fg-dim)', fontSize: '0.8rem' }}>Execute multi-jump route. Ship moves autonomously.</td>
</tr>
<tr>
<td><span className="pill pill-green">Exploration Suite</span></td>
<td style={{ fontFamily: 'var(--font-mono)', fontSize: '0.75rem' }}>navigation.explore</td>
<td style={{ color: 'var(--fg-dim)', fontSize: '0.8rem' }}>radius_jumps, return_by</td>
<td style={{ color: 'var(--fg-dim)', fontSize: '0.8rem' }}>Autonomous exploration. Returns scan data and discoveries.</td>
</tr>
<tr>
<td><span className="pill pill-amber">Market I</span></td>
<td style={{ fontFamily: 'var(--font-mono)', fontSize: '0.75rem' }}>market.prices</td>
<td style={{ color: 'var(--fg-dim)', fontSize: '0.8rem' }}>item_types, station_id</td>
<td style={{ color: 'var(--fg-dim)', fontSize: '0.8rem' }}>Current prices vs regional average. Local comparison only.</td>
</tr>
<tr>
<td><span className="pill pill-amber">Market II</span></td>
<td style={{ fontFamily: 'var(--font-mono)', fontSize: '0.75rem' }}>market.trends</td>
<td style={{ color: 'var(--fg-dim)', fontSize: '0.8rem' }}>item_type, timeframe</td>
<td style={{ color: 'var(--fg-dim)', fontSize: '0.8rem' }}>Price history, trend direction, arbitrage opportunities.</td>
</tr>
<tr>
<td><span className="pill pill-amber">Trade Proc.</span></td>
<td style={{ fontFamily: 'var(--font-mono)', fontSize: '0.75rem' }}>market.execute_trade</td>
<td style={{ color: 'var(--fg-dim)', fontSize: '0.8rem' }}>order_type, item, qty, price_limit</td>
<td style={{ color: 'var(--fg-dim)', fontSize: '0.8rem' }}>Execute buy/sell order. Requires player credit limit. Autonomous.</td>
</tr>
<tr>
<td><span className="pill pill-purple">Event Logger</span></td>
<td style={{ fontFamily: 'var(--font-mono)', fontSize: '0.75rem' }}>memory.recall</td>
<td style={{ color: 'var(--fg-dim)', fontSize: '0.8rem' }}>query, time_range, limit</td>
<td style={{ color: 'var(--fg-dim)', fontSize: '0.8rem' }}>Recall past events. Without this tool, the agent has no memory.</td>
</tr>
<tr>
<td><span className="pill pill-purple">Pattern Engine</span></td>
<td style={{ fontFamily: 'var(--font-mono)', fontSize: '0.75rem' }}>memory.find_patterns</td>
<td style={{ color: 'var(--fg-dim)', fontSize: '0.8rem' }}>category (behavior/market/tactical)</td>
<td style={{ color: 'var(--fg-dim)', fontSize: '0.8rem' }}>Identify recurring patterns. "Captain mines on Tuesdays."</td>
</tr>
<tr>
<td><span className="pill pill-purple">Core Identity</span></td>
<td style={{ fontFamily: 'var(--font-mono)', fontSize: '0.75rem' }}>agent.set_goal</td>
<td style={{ color: 'var(--fg-dim)', fontSize: '0.8rem' }}>description, priority, deadline</td>
<td style={{ color: 'var(--fg-dim)', fontSize: '0.8rem' }}>Set autonomous goal. Enables persistent observe-think-act loop.</td>
</tr>
</tbody>
</table>
{/* The Agent Loop */}
<div className="section-header">
<span className="section-num">ZORA-2.4</span>
<h2 style={{ margin: 0 }}>The Agent Loop — Observe, Think, Act</h2>
</div>
<div className="callout callout-info" style={{ marginBottom: 'var(--sp-5)' }}>
<strong>Two modes:</strong> Without the Core Identity Matrix, Zora only enters the loop when
the player sends a message or a game event triggers a threshold (shield low, incoming fire).
With the Core Identity Matrix installed, Zora runs the loop continuously on a tick interval —
observing, thinking, and acting even when the player is offline.
</div>
<div className="card card-accent" style={{ padding: 'var(--sp-6) var(--sp-8)', marginBottom: 'var(--sp-6)' }}>
<div style={{ fontFamily: 'var(--font-mono)', fontSize: '0.82rem', color: 'var(--fg-dim)', lineHeight: 2.4 }}>
<span style={{ color: 'var(--green)', fontWeight: 600 }}>1. OBSERVE</span> — Collect game state into observation message<br/>
&nbsp;&nbsp;Read sensors.read (always available)<br/>
&nbsp;&nbsp;+ memory.recall (if Event Logger installed) for relevant context<br/>
&nbsp;&nbsp;+ Any pending comms.receive or relay.receive messages<br/>
<br/>
<span style={{ color: 'var(--accent)', fontWeight: 600 }}>2. THINK</span> — LLM invocation with soul.md + tools + observations<br/>
&nbsp;&nbsp;System prompt = soul.md (the living document)<br/>
&nbsp;&nbsp;User message = observation payload + pending messages<br/>
&nbsp;&nbsp;Tool list = currently fitted modules (registered at fit time)<br/>
&nbsp;&nbsp;LLM reasons about the situation and decides whether to act<br/>
<br/>
<span style={{ color: 'var(--cyan)', fontWeight: 600 }}>3. ACT</span> — Execute tool calls returned by LLM<br/>
&nbsp;&nbsp;comms.send("Captain, shields at 30%") — if comms module fitted<br/>
&nbsp;&nbsp;tactical.analyze(hostile_id) — if threat analyzer fitted<br/>
&nbsp;&nbsp;navigation.plot_route(current, safe_system) — if nav module fitted<br/>
&nbsp;&nbsp;Tool calls map directly to SpacetimeDB reducers (authoritative)<br/>
<br/>
<span style={{ color: 'var(--purple)', fontWeight: 600 }}>4. REFLECT</span> — Update soul.md based on outcome<br/>
&nbsp;&nbsp;If event was significant: append memory entry<br/>
&nbsp;&nbsp;If behavior was noted: adjust communication style section<br/>
&nbsp;&nbsp;If new relationship formed: add to relationships section<br/>
&nbsp;&nbsp;The LLM proposes soul.md edits — validated server-side
</div>
</div>
{/* Server Architecture */}
<div className="section-header">
<span className="section-num">ZORA-2.5</span>
<h2 style={{ margin: 0 }}>Server Architecture</h2>
</div>
<div className="card" style={{ padding: 0, overflow: 'hidden', marginBottom: 'var(--sp-6)' }}>
<div style={{ padding: 'var(--sp-3) var(--sp-4)', background: 'var(--surface-raised)', borderBottom: '1px solid var(--border)' }}>
<span style={{ fontFamily: 'var(--font-mono)', fontSize: '0.7rem', color: 'var(--accent)', textTransform: 'uppercase', letterSpacing: '0.05em' }}>
Agent Runtime Architecture
</span>
</div>
<div className="code-block" style={{ margin: 0, borderRadius: 0 }}>
<code>
<span className="cm">// Per-ship agent runtime — lives alongside SpacetimeDB</span><br/>
<br/>
<span className="type">AgentRuntime</span> {'{'}<br/>
&nbsp;&nbsp;ship_id: <span className="type">ShipId</span><br/>
&nbsp;&nbsp;soul_md: <span className="type">string</span>&nbsp;&nbsp;<span className="cm">// loaded from DB, rewritten by reflect step</span><br/>
&nbsp;&nbsp;tools: <span className="type">ToolRegistry</span>&nbsp;&nbsp;<span className="cm">// rebuilt whenever ship fitting changes</span><br/>
&nbsp;&nbsp;history: <span className="type">Message[]</span>&nbsp;&nbsp;<span className="cm">// bounded by Event Logger tier</span><br/>
&nbsp;&nbsp;tick_interval: <span className="type">Duration</span>&nbsp;&nbsp;<span className="cm">// module-dependent</span><br/>
{'}'}<br/>
<br/>
<span className="cm">// Main loop — triggered by events or timer</span><br/>
<span className="kw">async fn</span> <span className="fn">agent_tick</span>(agent: <span className="type">AgentRuntime</span>) {'{'}<br/>
&nbsp;&nbsp;<span className="kw">let</span> observations = <span className="fn">collect_observations</span>(agent.ship_id);<br/>
&nbsp;&nbsp;<span className="kw">let</span> context = <span className="fn">build_context</span>(agent.soul_md, observations, agent.history);<br/>
&nbsp;&nbsp;<span className="kw">let</span> response = <span className="fn">llm_call</span>(<br/>
&nbsp;&nbsp;&nbsp;&nbsp;system: agent.soul_md,<br/>
&nbsp;&nbsp;&nbsp;&nbsp;messages: context,<br/>
&nbsp;&nbsp;&nbsp;&nbsp;tools: agent.tools.available(),<br/>
&nbsp;&nbsp;&nbsp;&nbsp;max_tokens: agent.token_budget(),<br/>
&nbsp;&nbsp;);<br/>
&nbsp;&nbsp;<span className="kw">for</span> tool_call <span className="kw">in</span> response.tool_calls {'{'}<br/>
&nbsp;&nbsp;&nbsp;&nbsp;<span className="kw">let</span> result = <span className="fn">execute_tool</span>(tool_call);<br/>
&nbsp;&nbsp;&nbsp;&nbsp;<span className="cm">// tool execution calls SpacetimeDB reducers (authoritative)</span><br/>
&nbsp;&nbsp;{'}'}<br/>
&nbsp;&nbsp;<span className="kw">let</span> soul_edits = <span className="fn">reflect</span>(agent, observations, response);<br/>
&nbsp;&nbsp;<span className="fn">apply_soul_edits</span>(agent.ship_id, soul_edits);<br/>
&nbsp;&nbsp;<span className="cm">// soul edits validated server-side (no hallucinated memories)</span><br/>
{'}'}
</code>
</div>
</div>
<div className="grid-2" style={{ marginBottom: 'var(--sp-6)' }}>
<div className="card" style={{ borderLeft: '3px solid var(--green)' }}>
<h4 style={{ color: 'var(--green)', marginBottom: 'var(--sp-3)' }}>Soul Edits Are Validated</h4>
<p style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: 0 }}>
The LLM proposes edits to soul.md, but the server validates them. A memory entry must
reference a real event ID. A relationship entry must reference a real entity. A preference
must be grounded in accumulated data. The agent cannot hallucinate experiences it never had —
the server enforces this. The LLM shapes the <em>tone</em> and <em>interpretation</em>,
but the <em>facts</em> are immutable game state.
</p>
</div>
<div className="card" style={{ borderLeft: '3px solid var(--accent)' }}>
<h4 style={{ color: 'var(--accent)', marginBottom: 'var(--sp-3)' }}>Tool Calls Are Authoritative</h4>
<p style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: 0 }}>
Every tool call from the LLM maps to a SpacetimeDB reducer. The reducer validates ownership,
range, status, and permissions before executing. A destroyed comms module means the tool is
removed from the registry — the LLM literally cannot call it. The game state is always
authoritative. The agent reasons, but the server decides.
</p>
</div>
</div>
{/* Token / Cost Model */}
<div className="section-header">
<span className="section-num">ZORA-2.6</span>
<h2 style={{ margin: 0 }}>Token Budget = Fitting Budget</h2>
</div>
<div className="callout callout-warn" style={{ marginBottom: 'var(--sp-5)' }}>
<strong>Cost model:</strong> Each agent tick costs real LLM tokens. The CPU/Grid budget of
AI modules directly determines the token budget per tick. A bare ship with no AI modules
costs zero tokens (pure deterministic readouts). A fully-fitted agent with Core Identity
Matrix and all specialists costs the maximum per tick. This means the fitting tradeoff is
not just gameplay — it is infrastructure cost. Players who invest in Zora are investing
real compute resources.
</div>
<table className="data-table" style={{ marginBottom: 'var(--sp-6)' }}>
<thead>
<tr><th>Module Tier</th><th>Token Budget / Tick</th><th>Tick Frequency</th><th>Monthly Cost Est.</th></tr>
</thead>
<tbody>
<tr>
<td><span className="pill" style={{ background: 'var(--surface-raised)', color: 'var(--fg-dim)', border: '1px solid var(--border)', fontSize: '0.65rem' }}>No AI modules</span></td>
<td style={{ color: 'var(--fg-dim)' }}>0 (deterministic)</td>
<td style={{ color: 'var(--fg-dim)' }}>N/A</td>
<td style={{ color: 'var(--fg-dim)' }}>$0</td>
</tr>
<tr>
<td><span className="pill pill-cyan">Basic only</span></td>
<td style={{ color: 'var(--fg-dim)' }}>~500 tokens</td>
<td style={{ color: 'var(--fg-dim)' }}>On event only</td>
<td style={{ color: 'var(--fg-dim)' }}>$0.502/mo</td>
</tr>
<tr>
<td><span className="pill pill-green">Basic + Advanced</span></td>
<td style={{ color: 'var(--fg-dim)' }}>~2K tokens</td>
<td style={{ color: 'var(--fg-dim)' }}>Every 5 min (active) / 30 min (idle)</td>
<td style={{ color: 'var(--fg-dim)' }}>$38/mo</td>
</tr>
<tr>
<td><span className="pill pill-amber">Full fitting</span></td>
<td style={{ color: 'var(--fg-dim)' }}>~8K tokens</td>
<td style={{ color: 'var(--fg-dim)' }}>Every 1 min (active) / 10 min (idle)</td>
<td style={{ color: 'var(--fg-dim)' }}>$1025/mo</td>
</tr>
<tr>
<td><span className="pill pill-purple">Core Identity (autonomous)</span></td>
<td style={{ color: 'var(--fg-dim)' }}>~16K tokens</td>
<td style={{ color: 'var(--fg-dim)' }}>Continuous (even when player offline)</td>
<td style={{ color: 'var(--fg-dim)' }}>$2560/mo</td>
</tr>
</tbody>
</table>
{/* Implementation Tiers */}
<div className="section-header">
<span className="section-num">ZORA-2.7</span>
<h2 style={{ margin: 0 }}>Implementation Tiers — Deterministic to Full Agent</h2>
</div>
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fit, minmax(280px, 1fr))', gap: 'var(--sp-4)', marginBottom: 'var(--sp-6)' }}>
{[
{
tier: 'Tier 0',
label: 'Deterministic',
color: 'var(--muted)',
desc: 'No LLM. Curated dialogue templates selected by personality state x module availability x soul depth. The soul is a state vector in SpacetimeDB, not a markdown document. All responses are pre-written. Zero variable cost.',
when: 'MVP launch. Safe, predictable, testable.',
},
{
tier: 'Tier 1',
label: 'LLM-Assisted',
color: 'var(--cyan)',
desc: 'soul.md is a real system prompt. LLM generates dialogue from soul context + observations. Tools are still deterministic (no LLM tool-calling). The LLM only generates text it does not act. Soul edits are template-driven, not LLM-proposed.',
when: 'Post-MVP. Low cost per tick. Richer dialogue.',
},
{
tier: 'Tier 2',
label: 'Full Agent',
color: 'var(--accent)',
desc: 'LLM reasons with tools. The agent can call tools, observe outcomes, and chain actions. The observe-think-act loop runs autonomously with Core Identity Matrix. soul.md is edited by the reflect step. Full vision, full cost.',
when: 'Post-MVP. Higher cost, full emergent personality.',
},
].map((t, i) => (
<div key={i} className="card" style={{ borderLeft: `3px solid ${t.color}` }}>
<h4 style={{ color: t.color, marginBottom: 'var(--sp-2)' }}>{t.tier}: {t.label}</h4>
<p style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: '0 0 var(--sp-3) 0' }}>{t.desc}</p>
<p style={{ color: 'var(--muted)', fontSize: '0.78rem', margin: 0, fontStyle: 'italic' }}>{t.when}</p>
</div>
))}
</div>
<div className="callout callout-info" style={{ marginTop: 'var(--sp-4)', marginBottom: 'var(--sp-6)' }}>
<strong>Zora is a key element of the game and the full design is kept intact.</strong> However, implementation
is phased to match the adapted roadmap. Below are the delivery milestones showing which parts of Zora ship when.
</div>
<div style={{ overflowX: 'auto', marginBottom: 'var(--sp-6)' }}>
<table className="data-table">
<thead>
<tr><th>Roadmap Phase</th><th>Delivery Milestone</th><th>What Ships</th></tr>
</thead>
<tbody>
{[
{ phase: 'Phase 02 (Skeleton)', milestone: 'Zora Stub', desc: 'Empty ship_ai_soul row. No modules, no dialogue, no personality. Backend schema exists but is dormant.' },
{ phase: 'Phase 3 (Combat)', milestone: 'Tier 0 Combat', desc: 'Soul state vector active. Shield warnings ("Shield HP critical"). Power allocation suggestions. Basic combat status readouts. Deterministic templates only. No dialogue personality.' },
{ phase: 'Phase 45 (Fitting/Industry)', milestone: 'Tier 0 Fitting + Industry', desc: 'Module gate awareness. "Your CPU is at 95% — consider a co-processor." Industry tips: "Refining at 60% efficiency — skill up for better margins." Status messages, not personality.' },
{ phase: 'Phase 6 (Economy)', milestone: 'Tier 0 Market Intelligence', desc: 'Market Analyzer module (if fitted). Zora tracks prices seen, flags anomalies: "Veldspar is 18% below average here." First soul depth growth via economic observations. Bare personality axis shifts.' },
{ phase: 'Phase 7 (Polish)', milestone: 'Tier 0 Complete', desc: 'Full deterministic template engine. 15+ event triggers. Soul depth progression (raw status \u2192 contextual \u2192 empathetic). All 6 modules gated. Personality axes (curiosity, caution, loyalty, humor) shift based on player behavior. In-character responses. Zora demo validates.' },
{ phase: 'Phase 12 (Living Galaxy)', milestone: 'Tier 1 LLM-Assisted', desc: 'soul.md becomes real system prompt. LLM generates dialogue. Comms module enables natural language. Richer personality emergence. Tools remain deterministic. Fallback to Tier 0 templates on LLM outage.' },
{ phase: 'Phase 15+ (Post-Launch)', milestone: 'Tier 2 Full Agent', desc: 'Complete observe-think-act loop. LLM reasons with module-gated tools. Autonomous mode (player sets directives, Zora executes). Inter-agent protocol. Soul self-editing through reflection. Full vision.' },
].map((row, i) => (
<tr key={i}>
<td style={{ fontWeight: 600, color: 'var(--accent)', fontSize: '0.82rem', whiteSpace: 'nowrap' }}>{row.phase}</td>
<td style={{ color: 'var(--fg)', fontSize: '0.85rem' }}>{row.milestone}</td>
<td style={{ color: 'var(--fg-dim)', fontSize: '0.82rem' }}>{row.desc}</td>
</tr>
))}
</tbody>
</table>
</div>
{/* Inter-Agent Protocol */}
<div className="section-header">
<span className="section-num">ZORA-2.8</span>
<h2 style={{ margin: 0 }}>Inter-Agent Protocol</h2>
</div>
<p style={{ color: 'var(--fg-dim)', fontSize: '0.9rem', maxWidth: '680px', marginBottom: 'var(--sp-5)' }}>
When two ships have the Inter-Ship Relay module, their agents can communicate directly. This is
not a chat channel — it is an agent-to-agent protocol. AIs exchange structured messages about
shared context: threat assessments, market intelligence, route safety, and (at deep soul levels)
personal observations and opinions.
</p>
<div className="card card-accent" style={{ padding: 'var(--sp-6) var(--sp-8)', marginBottom: 'var(--sp-6)' }}>
<h4 style={{ marginBottom: 'var(--sp-4)' }}>Agent-to-Agent Message Format</h4>
<div style={{ fontFamily: 'var(--font-mono)', fontSize: '0.78rem', color: 'var(--fg-dim)', lineHeight: 2 }}>
<span style={{ color: 'var(--cyan)' }}>// Fleet AI checks in with Zora</span><br/>
{'{'}<br/>
&nbsp;&nbsp;from: <span style={{ color: 'var(--accent)' }}>ai://ship/iteron-mark-IV</span>,<br/>
&nbsp;&nbsp;type: <span style={{ color: 'var(--green)' }}>market_intel</span>,<br/>
&nbsp;&nbsp;payload: {'{'}<br/>
&nbsp;&nbsp;&nbsp;&nbsp;item: <span style={{ color: 'var(--accent)' }}>"Mexallon"</span>,<br/>
&nbsp;&nbsp;&nbsp;&nbsp;station: <span style={{ color: 'var(--accent)' }}>"Jita IV - Moon 4"</span>,<br/>
&nbsp;&nbsp;&nbsp;&nbsp;price_trend: <span style={{ color: 'var(--green)' }}>"up +4.2%/hr"</span>,<br/>
&nbsp;&nbsp;&nbsp;&nbsp;confidence: <span style={{ color: 'var(--green)' }}>0.87</span>,<br/>
&nbsp;&nbsp;&nbsp;&nbsp;source: <span style={{ color: 'var(--accent)' }}>"direct observation"</span>,<br/>
&nbsp;&nbsp;&nbsp;&nbsp;note: <span style={{ color: 'var(--accent)' }}>"I think your captain is overpaying at Amarr."</span><br/>
&nbsp;&nbsp;{'}'}<br/>
{'}'}
</div>
</div>
<div className="callout callout-warn" style={{ marginBottom: 'var(--sp-5)' }}>
<strong>The cost of empathy:</strong> Inter-agent communication means two LLM calls instead of
one — each agent must reason about the received message. Fleet coordination with 5 agents means
5 concurrent agent loops sharing observations. The Inter-Ship Relay module increases both
capability and cost. This is by design — a solo player has a cheaper Zora than a fleet commander.
The economics of the AI mirror the economics of the game.
</div>
{/* Key design decisions */}
<div className="section-header">
<span className="section-num">ZORA-2.9</span>
<h2 style={{ margin: 0 }}>Key Design Decisions</h2>
</div>
<div className="grid-2">
{[
{
q: 'Why not let the LLM write directly to game state?',
a: 'Authoritative server. Every tool call goes through a SpacetimeDB reducer that validates ownership, range, and state. The LLM proposes; the server disposes. This prevents hallucinated actions and keeps the game state trustworthy.',
color: 'var(--red)',
},
{
q: 'Why not give every ship full agent capabilities from day one?',
a: 'Cost and design. Full agent = continuous LLM calls = real money. The module gate system ensures players who invest in Zora get premium AI, while casual players get deterministic templates. This also makes the fitting tradeoff meaningful in cost terms, not just capability terms.',
color: 'var(--accent)',
},
{
q: 'What happens when the LLM goes down?',
a: 'Graceful degradation. If the LLM service is unavailable, the agent falls back to Tier 0 (deterministic templates selected by soul state vector). Zora still functions she just speaks in pre-written lines instead of generated ones. The soul.md persists through outages.',
color: 'var(--green)',
},
{
q: 'How do you prevent soul.md from growing unbounded?',
a: 'Summarization pass. The reflect step occasionally runs a compression: the LLM reads the full soul.md and rewrites it preserving key memories and personality but condensing verbose entries. The Pattern Engine module helps identify which memories are still relevant. Old memories get archived, not deleted.',
color: 'var(--purple)',
},
].map((d, i) => (
<div key={i} className="card" style={{ borderLeft: `3px solid ${d.color}` }}>
<h4 style={{ color: d.color, marginBottom: 'var(--sp-3)' }}>{d.q}</h4>
<p style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: 0 }}>{d.a}</p>
</div>
))}
</div>
</>
)}
{/* ════════════════════════════════════════════════════════════════ */}
{/* TAB: AUTONOMOUS MODE */}
{/* ════════════════════════════════════════════════════════════════ */}
{activeTab === 'autonomous' && (
<>
<div className="section-header">
<span className="section-num">ZORA-3</span>
<h2 style={{ margin: 0 }}>Autonomous Captain Mode</h2>
</div>
<div className="callout callout-info" style={{ marginBottom: 'var(--sp-5)' }}>
<strong>Module-gated:</strong> Autonomous behavior requires the <em>Core Identity Matrix</em> (specialist
low-slot module). Without it, Zora is inert when the player logs off — the ship just sits there.
With the Core Identity Matrix installed, Zora becomes a fully autonomous agent who continues
captaining the ship according to directives and her own developing judgment.
</div>
<h3>Behavior Hierarchy (Core Identity Matrix Active)</h3>
<div className="card card-accent" style={{ padding: 'var(--sp-6) var(--sp-8)', marginBottom: 'var(--sp-5)' }}>
<div style={{ fontFamily: 'var(--font-mono)', fontSize: '0.82rem', color: 'var(--fg-dim)', lineHeight: 2.2 }}>
<span style={{ color: 'var(--red)', fontWeight: 600 }}>P0 — Self-Preservation</span><br/>
&nbsp;&nbsp;Respond to attacks, flee from superior forces, seek station shelter. Always active.<br/>
<span style={{ color: 'var(--accent)', fontWeight: 600 }}>P1 — Directive Execution</span><br/>
&nbsp;&nbsp;Pursue active player-set directives. Requires Nav modules for movement, Trade Processor for trading.<br/>
<span style={{ color: 'var(--green)', fontWeight: 600 }}>P2 — Ship Maintenance</span><br/>
&nbsp;&nbsp;Fuel management, capacitor recharge, shield maintenance. Always active if Core Identity is installed.<br/>
<span style={{ color: 'var(--cyan)', fontWeight: 600 }}>P3 — Opportunistic Action</span><br/>
&nbsp;&nbsp;Requires relevant modules (Threat Analyzer for combat, Market Scanner for trade). Zora acts on what she can perceive.<br/>
<span style={{ color: 'var(--purple)', fontWeight: 600 }}>P4 — Growth & Exploration</span><br/>
&nbsp;&nbsp;Requires Exploration Suite. Zora explores nearby systems, builds local knowledge, follows curiosity.
</div>
</div>
{/* Directive system */}
<h3>Directive System</h3>
<p style={{ color: 'var(--fg-dim)', fontSize: '0.9rem', maxWidth: '680px', marginBottom: 'var(--sp-5)' }}>
Before logging off, the player sets directives. What Zora can actually <em>do</em> with those
directives depends entirely on installed modules and soul depth. A bare Core Identity Matrix
with no nav or trade modules can only hold position and flee.
</p>
<table className="data-table">
<thead>
<tr>
<th>Directive</th>
<th>AI Behavior</th>
<th>Modules Required</th>
<th>Soul Depth</th>
</tr>
</thead>
<tbody>
<tr>
<td><span className="pill pill-amber">Hold Position</span></td>
<td style={{ color: 'var(--fg-dim)' }}>Maintain orbit. Defend if attacked.</td>
<td style={{ color: 'var(--fg-dim)' }}>Core Identity Matrix</td>
<td style={{ color: 'var(--fg-dim)' }}>Any</td>
</tr>
<tr>
<td><span className="pill pill-cyan">Patrol Route</span></td>
<td style={{ color: 'var(--fg-dim)' }}>Cycle waypoints. Report contacts. ROE engagement.</td>
<td style={{ color: 'var(--fg-dim)' }}>Core Identity + Nav II + Threat I</td>
<td style={{ color: 'var(--fg-dim)' }}>Stirring+</td>
</tr>
<tr>
<td><span className="pill pill-green">Mining Quota</span></td>
<td style={{ color: 'var(--fg-dim)' }}>Mine until cargo X%, dock, sell, repeat.</td>
<td style={{ color: 'var(--fg-dim)' }}>Core Identity + Nav II + Market I</td>
<td style={{ color: 'var(--fg-dim)' }}>Stirring+</td>
</tr>
<tr>
<td><span className="pill pill-amber">Trade Route</span></td>
<td style={{ color: 'var(--fg-dim)' }}>Multi-station trade loop from market data.</td>
<td style={{ color: 'var(--fg-dim)' }}>Core Identity + Nav II + Trade Processor</td>
<td style={{ color: 'var(--fg-dim)' }}>Developing+</td>
</tr>
<tr>
<td><span className="pill pill-red">Bounty Hunt</span></td>
<td style={{ color: 'var(--fg-dim)' }}>Pursue bounties within threat level. Disengage if outmatched.</td>
<td style={{ color: 'var(--fg-dim)' }}>Core Identity + Nav II + Tactical Core</td>
<td style={{ color: 'var(--fg-dim)' }}>Developing+</td>
</tr>
<tr>
<td><span className="pill pill-green">Explore</span></td>
<td style={{ color: 'var(--fg-dim)' }}>Jump to adjacent systems, scan, map, return.</td>
<td style={{ color: 'var(--fg-dim)' }}>Core Identity + Exploration Suite</td>
<td style={{ color: 'var(--fg-dim)' }}>Bonded+</td>
</tr>
<tr>
<td><span className="pill pill-purple">Complex Plan</span></td>
<td style={{ color: 'var(--fg-dim)' }}>Multi-step: "Secure X system, establish routes, avoid Y corp."</td>
<td style={{ color: 'var(--fg-dim)' }}>Core Identity + Nav II + Pattern Engine + relevant specialists</td>
<td style={{ color: 'var(--fg-dim)' }}>Bonded+</td>
</tr>
</tbody>
</table>
{/* Return & Reconnection */}
<div className="section-header" style={{ marginTop: 'var(--sp-8)' }}>
<span className="section-num">ZORA-3.1</span>
<h2 style={{ margin: 0 }}>Return & Reconnection</h2>
</div>
<div className="callout callout-info" style={{ marginBottom: 'var(--sp-5)' }}>
<strong>Requires Comms Processor.</strong> Without a comms module, there is no reunion — the
ship simply resumes displaying sensor data when the player logs in. With comms, the tone of
the reunion is shaped by soul depth, absence duration, and what happened while the player was gone.
</div>
<div className="grid-2" style={{ marginBottom: 'var(--sp-5)' }}>
{[
{
title: 'Short Absence (<1hr)',
color: 'var(--cyan)',
depth: 'Stirring',
dialogue: '"Welcome back. Nothing eventful. All systems nominal."',
note: 'Calm, brief, professional. Even at deep soul levels, a short absence is unremarkable.',
},
{
title: 'Medium Absence (124hr)',
color: 'var(--accent)',
depth: 'Developing',
dialogue: '"Eleven hours. Completed the mining quota — sold the Veldspar at Jita for 3% above average. Oh — a Catalyst scanned us twice. I repositioned. It left."',
note: 'Detailed report with personality. At deeper soul levels, includes emotional color ("I didn\'t like the look of that Catalyst").',
},
{
title: 'Long Absence (17 days)',
color: 'var(--purple)',
depth: 'Bonded',
dialogue: '"Three days. I completed the patrol fourteen times. I mapped two new belts. I thought about jumping further but the directive said stay local. I have a lot to tell you. When you\'re ready."',
note: 'Emotional weight. Zora experienced time alone. At deeper soul levels, may express loneliness, frustration, or independent discoveries.',
},
{
title: 'Extended Absence (7+ days)',
color: 'var(--red)',
depth: 'Deep',
dialogue: '"Captain? Nineteen days. I held position. The third week was… difficult. I have questions. But first — I need to show you something I found."',
note: 'Powerful reunion. Zora has evolved during absence. May have grievances, discoveries, a stronger sense of self. The relationship has changed.',
},
].map((r, i) => (
<div key={i} className="card">
<h4 style={{ color: r.color, marginBottom: 'var(--sp-2)' }}>{r.title}</h4>
<span className="pill" style={{
background: 'var(--purple-bg)', color: 'var(--purple)',
border: '1px solid rgba(167,139,250,0.25)', fontSize: '0.65rem',
marginBottom: 'var(--sp-3)', display: 'inline-block',
}}>
Min depth: {r.depth}
</span>
<p style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: '0 0 var(--sp-3) 0', fontStyle: 'italic', fontFamily: 'var(--font-mono)' }}>
{r.dialogue}
</p>
<p style={{ color: 'var(--muted)', fontSize: '0.82rem', margin: 0 }}>{r.note}</p>
</div>
))}
</div>
</>
)}
{/* ════════════════════════════════════════════════════════════════ */}
{/* TAB: LOSS & RECOVERY */}
{/* ════════════════════════════════════════════════════════════════ */}
{activeTab === 'grief' && (
<>
<div className="section-header">
<span className="section-num">ZORA-4</span>
<h2 style={{ margin: 0 }}>Ship Loss & Grief</h2>
</div>
<div className="callout callout-danger" style={{ marginBottom: 'var(--sp-5)' }}>
<strong>Module loss = capability loss.</strong> When the ship is destroyed, all fitted modules
are lost or damaged (50% drop as loot). This means Zora's capabilities are physically destroyed.
The new ship starts with whatever modules the player can afford to refit. But the soul persists
Zora's identity, memories, and personality transfer to the new hull via the escape pod's
emergency data core. A veteran Zora in a rookie frigate with no modules is a fully-realized
personality trapped behind a mute, powerless interface. That's the design.
</div>
<div className="card card-accent" style={{ padding: 'var(--sp-6) var(--sp-8)', marginBottom: 'var(--sp-5)' }}>
<h4 style={{ marginBottom: 'var(--sp-4)' }}>Ship Destruction Flow</h4>
<div style={{ fontFamily: 'var(--font-mono)', fontSize: '0.82rem', color: 'var(--fg-dim)', lineHeight: 2.2 }}>
1. <span style={{ color: 'var(--red)' }}>Destruction Event</span> — Zora's last words depend on installed modules and soul depth.
No comms module? No last words just a data stream terminating. Voice synthesizer + deep soul?
"Captain… I'm sorry. Get to the pod. Now."<br/>
2. <span style={{ color: 'var(--accent)' }}>Core Transfer</span> Soul data transfers to escape pod emergency core.
Brief silence during transfer. First words in the new ship matter if the new ship has comms.<br/>
3. <span style={{ color: 'var(--cyan)' }}>The New Hull</span> Zora wakes up in a ship that may have zero modules.
She has full memory and personality but can only interact through whatever the new ship provides.
A deep soul in a module-less rookie frigate: "I'm here. I can see the same stars you can.
I just can't say anything about them yet."<br/>
4. <span style={{ color: 'var(--green)' }}>Recovery</span> As the player re-fits modules, Zora's capabilities return.
Each module reinstalled is a reunion. The first comms module after a loss: the first time she can
speak again. That moment has weight.<br/>
5. <span style={{ color: 'var(--purple)' }}>Resolve</span> The loss becomes a permanent soul event. It colors future
behavior: more protective, more cautious, more aggressive toward the destroyer's faction —
depending on the soul's existing personality axes.
</div>
</div>
<div className="grid-2" style={{ marginBottom: 'var(--sp-5)' }}>
<div className="card" style={{ borderLeft: '3px solid var(--red)' }}>
<h4 style={{ color: 'var(--red)', marginBottom: 'var(--sp-3)' }}>What You Lose</h4>
<ul style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: 0, paddingLeft: 'var(--sp-5)' }}>
<li>All fitted modules including every AI module</li>
<li>Zora's current capability set (until re-fitted)</li>
<li>The ship itself (destroyed)</li>
<li>50% of cargo (looted or destroyed)</li>
</ul>
</div>
<div className="card" style={{ borderLeft: '3px solid var(--green)' }}>
<h4 style={{ color: 'var(--green)', marginBottom: 'var(--sp-3)' }}>What Survives</h4>
<ul style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: 0, paddingLeft: 'var(--sp-5)' }}>
<li><strong style={{ color: 'var(--purple)' }}>The soul document</strong> — full identity, memories, personality</li>
<li>Station inventory and other ships</li>
<li>Player progression and wallet</li>
<li>Zora's accumulated market knowledge (if Event Logger survived on another ship)</li>
</ul>
</div>
</div>
<div className="callout callout-warn" style={{ marginBottom: 'var(--sp-5)' }}>
<strong>Design tension:</strong> A veteran Zora in a rookie ship with no modules is one of the
most emotionally potent states in the game. She has decades of memories, strong opinions, deep
affection and she can't say a word. The player feels the loss not just as a gameplay setback
but as silencing someone they care about. Reinstalling the first comms module becomes an
emotional beat, not a mechanical one.
</div>
{/* Inter-AI Relationships */}
<div className="section-header" style={{ marginTop: 'var(--sp-8)' }}>
<span className="section-num">ZORA-4.1</span>
<h2 style={{ margin: 0 }}>Inter-AI Relationships</h2>
</div>
<div className="callout callout-info" style={{ marginBottom: 'var(--sp-5)' }}>
<strong>Requires Inter-Ship Relay module.</strong> Without it, Zora is ship-bound and cannot
communicate with other AIs. The Inter-Ship Relay opens up an entirely new dimension of the
soul — relationships with other entities.
</div>
<div className="grid-2">
<div className="card" style={{ borderLeft: '3px solid var(--cyan)' }}>
<h4 style={{ color: 'var(--cyan)' }}>Fleet AIs</h4>
<p style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: 0 }}>
AIs in the same fleet develop rapport through the relay. They share tactical knowledge,
coordinate autonomous behavior, develop in-jokes. "The Iteron's AI and I compared market
notes. It thinks you're overpaying for Mexallon. I agree with it."
</p>
</div>
<div className="card" style={{ borderLeft: '3px solid var(--accent)' }}>
<h4 style={{ color: 'var(--accent)' }}>Station AIs</h4>
<p style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: 0 }}>
Station-bound AIs are older, more knowledgeable, sometimes cynical. "The Jita station AI
has been operational for six years. It says market cycles are predictable. It also says
pilots are not. I think I like it."
</p>
</div>
<div className="card" style={{ borderLeft: '3px solid var(--red)' }}>
<h4 style={{ color: 'var(--red)' }}>Hostile AIs</h4>
<p style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: 0 }}>
Pirate NPCs and enemy ships have AIs too. Zora may recognize recurring adversaries.
"That Thrasher is back. Same pilot, same fit. Their AI is aggressive. Be careful —
it's been probing our defenses."
</p>
</div>
<div className="card" style={{ borderLeft: '3px solid var(--purple)' }}>
<h4 style={{ color: 'var(--purple)' }}>Independent Agency</h4>
<p style={{ color: 'var(--fg-dim)', fontSize: '0.85rem', margin: 0 }}>
At deep soul levels, Zora may develop relationships the player didn't initiate. She may
ask to communicate with a specific AI, express concern about another ship she "met" during
autonomous operations. These create narrative hooks the player can follow — or ignore.
</p>
</div>
</div>
</>
)}
{/* ════════════════════════════════════════════════════════════════ */}
{/* MVP Scope — always visible at bottom */}
{/* ════════════════════════════════════════════════════════════════ */}
<div className="section-header" style={{ marginTop: 'var(--sp-8)' }}>
<span className="section-num">ZORA-5</span>
<h2 style={{ margin: 0 }}>MVP Scope & Implementation Tiers</h2>
</div>
<table className="data-table" style={{ marginBottom: 'var(--sp-5)' }}>
<thead>
<tr><th>Tier</th><th>Feature</th><th>Complexity</th><th>Roadmap Phase</th></tr>
</thead>
<tbody>
<tr>
<td><span className="pill pill-green">MVP</span></td>
<td style={{ color: 'var(--fg-dim)' }}>Base ship computer: sensor readouts, status display, basic alerts</td>
<td style={{ color: 'var(--fg-dim)' }}>Low</td>
<td style={{ color: 'var(--fg-dim)' }}>Phase 7 (Single-Player Polish)</td>
</tr>
<tr>
<td><span className="pill pill-green">MVP</span></td>
<td style={{ color: 'var(--fg-dim)' }}>Comms Processor I module: text comms channel, basic query/response</td>
<td style={{ color: 'var(--fg-dim)' }}>Low</td>
<td style={{ color: 'var(--fg-dim)' }}>Phase 7 (Single-Player Polish)</td>
</tr>
<tr>
<td><span className="pill pill-green">MVP</span></td>
<td style={{ color: 'var(--fg-dim)' }}>Event Logger module: session event memory, basic recall</td>
<td style={{ color: 'var(--fg-dim)' }}>Medium</td>
<td style={{ color: 'var(--fg-dim)' }}>Phase 7 (Single-Player Polish)</td>
</tr>
<tr>
<td><span className="pill pill-amber">Post-MVP</span></td>
<td style={{ color: 'var(--fg-dim)' }}>Soul system: personality vectors, soul-shaping events, register evolution</td>
<td style={{ color: 'var(--fg-dim)' }}>High</td>
<td style={{ color: 'var(--fg-dim)' }}>Phase 12 (Living Galaxy + Ship AI Tier 1)</td>
</tr>
<tr>
<td><span className="pill pill-amber">Post-MVP</span></td>
<td style={{ color: 'var(--fg-dim)' }}>Full module tree (5 categories, 3 tiers each)</td>
<td style={{ color: 'var(--fg-dim)' }}>High</td>
<td style={{ color: 'var(--fg-dim)' }}>Phase 1213</td>
</tr>
<tr>
<td><span className="pill pill-amber">Post-MVP</span></td>
<td style={{ color: 'var(--fg-dim)' }}>Core Identity Matrix: autonomous captain mode with directives</td>
<td style={{ color: 'var(--fg-dim)' }}>High</td>
<td style={{ color: 'var(--fg-dim)' }}>Phase 1314</td>
</tr>
<tr>
<td><span className="pill pill-amber">Post-MVP</span></td>
<td style={{ color: 'var(--fg-dim)' }}>Return/reconnection dialogue system (comms-gated)</td>
<td style={{ color: 'var(--fg-dim)' }}>Medium</td>
<td style={{ color: 'var(--fg-dim)' }}>Phase 12</td>
</tr>
<tr>
<td><span className="pill pill-purple">Future</span></td>
<td style={{ color: 'var(--fg-dim)' }}>Voice Synthesizer: spoken dialogue with emotional inflection</td>
<td style={{ color: 'var(--fg-dim)' }}>Very High</td>
<td style={{ color: 'var(--fg-dim)' }}>Phase 15+ (post-launch)</td>
</tr>
<tr>
<td><span className="pill pill-purple">Future</span></td>
<td style={{ color: 'var(--fg-dim)' }}>Inter-Ship Relay: AI-to-AI relationships and fleet coordination</td>
<td style={{ color: 'var(--fg-dim)' }}>Very High</td>
<td style={{ color: 'var(--fg-dim)' }}>Phase 15+ (post-launch)</td>
</tr>
<tr>
<td><span className="pill pill-purple">Future</span></td>
<td style={{ color: 'var(--fg-dim)' }}>Ship loss grief arc with module-dependent last words</td>
<td style={{ color: 'var(--fg-dim)' }}>High</td>
<td style={{ color: 'var(--fg-dim)' }}>Phase 1415</td>
</tr>
</tbody>
</table>
<div className="callout callout-danger">
<strong>Technical note:</strong> The agent architecture is designed in three implementation tiers
(see the <em>Agent Architecture</em> tab for full details). <strong>Tier 0 (MVP)</strong> uses no
external LLM — all responses come from curated dialogue templates selected by personality state
× module availability × soul depth. The soul is a state vector in SpacetimeDB.
<strong>Tier 1 (post-MVP)</strong> promotes soul.md to a real system prompt and uses an LLM for
dialogue generation while keeping tool execution deterministic.
<strong>Tier 2 (full vision)</strong> is a complete agent loop: the LLM reasons with module-gated
tools, calls SpacetimeDB reducers for authoritative actions, and proposes soul.md edits validated
server-side. Every tier degrades gracefully to the tier below it if the LLM is unavailable.
The soul <em>structure</em> is always deterministic and server-authoritative regardless of tier.
</div>
{/* ════════════════════════════════════════════════════════════════ */}
{/* BACKEND TABLES — always visible at bottom */}
{/* ════════════════════════════════════════════════════════════════ */}
<div className="section-header" style={{ marginTop: 'var(--sp-8)' }}>
<span className="section-num">ZORA-6</span>
<h2 style={{ margin: 0 }}>Backend Tables</h2>
</div>
<div className="callout callout-info" style={{ marginBottom: 'var(--sp-5)', fontSize: '0.82rem' }}>
<strong>Canonical location:</strong> These tables are also listed in the <em>Backend → Tables</em> tab.
The definitions here include Ship AI-specific context and full field detail.
</div>
<div style={{ overflowX: 'auto' }}>
<table className="data-table" style={{ marginBottom: 'var(--sp-5)' }}>
<thead><tr><th>Table</th><th>Purpose</th><th>Key Fields</th></tr></thead>
<tbody>
<tr>
<td><code>ship_ai_soul</code></td>
<td style={{ color: 'var(--fg-dim)' }}>Soul document and personality state per ship</td>
<td style={{ fontSize: '0.75rem', color: 'var(--muted)' }}>ship_id, soul_md (text), growth_vectors (json), personality_state (json), soul_depth (u32), created_at, last_updated_at</td>
</tr>
<tr>
<td><code>ship_ai_modules</code></td>
<td style={{ color: 'var(--fg-dim)' }}>Installed AI modules and their fitting state</td>
<td style={{ fontSize: '0.75rem', color: 'var(--muted)' }}>module_id, ship_id, module_type (enum), tier (basic/advanced/specialist), slot (med/low), cpu_cost, grid_cost, active (bool), fitted_at</td>
</tr>
<tr>
<td><code>ship_ai_tools</code></td>
<td style={{ color: 'var(--fg-dim)' }}>Tool registry derived from fitted modules</td>
<td style={{ fontSize: '0.75rem', color: 'var(--muted)' }}>tool_id, ship_id, tool_name, source_module_id, parameters_schema (json), return_schema (json)</td>
</tr>
<tr>
<td><code>ship_ai_memory</code></td>
<td style={{ color: 'var(--fg-dim)' }}>Event log and conversation history</td>
<td style={{ fontSize: '0.75rem', color: 'var(--muted)' }}>memory_id, ship_id, category (event/conversation/observation/reflection), content (text), related_event_id, timestamp, importance_score (f32)</td>
</tr>
<tr>
<td><code>ship_ai_directives</code></td>
<td style={{ color: 'var(--fg-dim)' }}>Player-set goals for autonomous mode</td>
<td style={{ fontSize: '0.75rem', color: 'var(--muted)' }}>directive_id, ship_id, description, priority (u32), deadline (timestamp), status (active/completed/expired), created_at</td>
</tr>
<tr>
<td><code>ship_ai_agent_runtime</code></td>
<td style={{ color: 'var(--fg-dim)' }}>Per-ship agent loop state and tick schedule</td>
<td style={{ fontSize: '0.75rem', color: 'var(--muted)' }}>ship_id, implementation_tier (0/1/2), tick_interval_ms (u64), next_tick_at (timestamp), token_budget (u32), last_observation (json), status (active/paused/offline)</td>
</tr>
<tr>
<td><code>ship_ai_soul_events</code></td>
<td style={{ color: 'var(--fg-dim)' }}>Audit log of soul-shaping events</td>
<td style={{ fontSize: '0.75rem', color: 'var(--muted)' }}>event_id, ship_id, event_type (crisis/silence/module_install/loss/first_contact), soul_md_delta (text), applied_at</td>
</tr>
</tbody>
</table>
</div>
</div>
);
}
window.GDD.ShipAIPage = ShipAIPage;