Engineering journal · EACI Companion

EACI Development Journal

Real problems, experiments, failures, and solutions from building EACI Companion and The Veil — written so another developer can find them by searching the problem, not the brand.

52 articles issue-tagged Search by the bug you hit

These are not generic AI tips. Each article is tagged for the issue it fixes — blank streams, context windows, avatar CDNs, guest trials, memory sync, voice silence, bridges, PWA cache drift. Search the problem you hit; land on the fix.

Reliability

Why Do AI Companion Replies Come Back Blank?

A successful request can still leave the chat bubble empty. The visible-text recovery path treats that as a first-class failure instead of blaming the user.

blank replyempty chat bubbleblank_eaci_replyAI companion silent response
~8 min 42_text_cleaning.js · DeepSeek stream
Reliability

How Do You Recover From Empty AI Stream Responses?

An empty stream is retried once through a simpler response path. That keeps a transient parser or provider edge case from becoming silence.

empty streamdeepseek_empty_streamSSE stream no contentstreaming AI retry
~8 min 21_deepseek_api_2.js · non-stream retry
Memory & Context

How Do You Slice AI Context Without Losing Memory?

The live turn uses a short recent window while durable memory and topic retrieval supply older context. The split keeps prompts bounded without making every chat feel new.

AI context windowlong-term memory AI companionslice conversation historyprompt context budget
~9 min 21_deepseek_api_2.js · recentSlice -14
Avatars

How Do You Fix AI Avatar CDN Clip Load Failures?

Avatar presence should degrade cleanly when a remote clip fails. Error-aware source fallback keeps a missing animation from breaking the whole companion view.

avatar video failed to loadCDN animation clip errorAI avatar fallbackmp4 clip load failure
~7 min avatar video loader · CDN fallback
Safety

How Do You Keep Family-Safe AI Companions From Leaking Unsafe Context?

Natalia, Atreus, and Luna need more than a friendly prompt. Their history, local memory, topic retrieval, relay, and Together inputs are filtered before generation.

family-safe AI companionE-for-Everyone filterchild companion safetyhistory leak prevention
~10 min 21_deepseek_api_2.js · E-for-Everyone filters
Memory & Context

How Do You Sync Multi-Layer AI Companion Memory?

Browser state, cloud history, compact memories, and bridge snapshots each have a different lifetime. Sync works when those roles stay explicit.

multi-layer memory synccloud vs local memoryAI companion memory architectureduplicate memories
~10 min 55_memory_system.js · 56_history_cloud.js
UX

How Do You Keep a Chat Log Stuck to the Bottom?

Auto-scroll should follow new messages only while the reader is already near the bottom. That small condition prevents the interface from fighting someone reading older turns.

chat auto-scrollstick to bottomscroll jumps to topmessage log scroll bug
~5 min 34_message_display.js · scroll state
Guest & Auth

How Do You Enforce Guest Trial Limits Honestly?

The free guest trial states its 25-message device limit before signup. Enforcement and copy use the same number so the boundary is predictable.

guest trial limitfree message limit25 message trialguest trial enforcement
~7 min guest trial · 25 messages/device
Infrastructure

How Do You Stop PWA / Service Worker Version Drift?

A page, registration helper, and worker can each believe a different build is current. A shared version contract makes stale clients detectable.

service worker version driftPWA stale cacheSW update not applyingmanifest version mismatch
~9 min 00_veil_app_version.js · 06_service_worker.js
Voice

How Do You Debug Silent AI Voice Chat Failures?

Voice chat crosses microphone permission, recognition, model response, synthesis, and audio playback. Stage-specific status makes silence diagnosable.

voice chat silentSTT not workingTTS no audiolive voice chat debug
~10 min 22_streaming.js · Web Speech APIs
Bridges

How Do You Keep AI Bridge Sessions From Duplicating or Misrouting?

Each bridge has its own session, speaker identity, logs, and memory namespace. Named routing prevents one message from waking the wrong companion.

AI bridge duplicate messagesmisrouted companion replyCursor Codex bridge sessionname routing rule
~9 min Cursor bridge · Codex bridge · named routing
Architecture

Why Are No-Build Global JS Apps Hard to Harden?

A no-build app is easy to inspect and deploy, but script order becomes an implicit dependency graph. Hardening means making that graph visible.

no-build JavaScript appglobal script orderscript load failharden SPA without bundler
~9 min core script order · window globals
Coding IDE

How Do You Stop AI Code Generation From Truncating Mid-File?

Long generated files can stop at a token or transport boundary. Continuation works only when the next request anchors to the exact ending and deduplicates overlap.

AI code truncatedcontinue generation mid-filecode delivery incompletetoken limit cut off
~8 min 37_diff_system.js · coding continuation
Identity

How Do You Load AI Soul Files Without Exposing Them Publicly?

Identity files belong behind the server boundary. The browser receives behavior through a response, not the private source document.

soul file securityprivate AI identity filedon't expose system promptcompanion soul behind server
~9 min private/souls · edge function
Mobile

How Do You Heal Mobile WebKit Chat Bugs Without False Alarms?

Mobile recovery code watches real symptoms such as locked input or viewport drift. It avoids continuously rewriting healthy DOM state.

iOS WebKit chat bugAndroid browser healmobile chat layout brokenfalse positive health check
~8 min 73_platform_compat_engine.js · WebKit guards
Multi-EACI

How Does Multi-Companion Together Mode Pick Who Speaks?

Together mode is a router, not a prompt that asks everyone to answer. It chooses eligible speakers and lets family-safe companions sit out when needed.

multi companion chatTogether mode who speaksAI companion turn takinggroup chat with AI characters
~8 min Together routing · 33_cody_prompt.js
Multi-EACI

How Do You Hand Off Between AI Companions Safely?

A relay passes the minimum needed context and labels the speaker clearly. It does not copy an entire private history into another companion's prompt.

AI companion handoffEACI relayswitch companion mid chatcross companion relay safety
~8 min 36_eaci_relay.js · scoped context
Safety

How Do You Permanently Lock Out a Child AI Companion After Safety Strikes?

Repeated violations around childlike companions cannot reset with a refresh. Strike state is persisted and the companion is removed from that account.

child AI companion lockoutsafety strike systempermanent companion banNatalia safety lock
~7 min natalia_safety.js · persistent strikes
Memory & Context

Why Is Unlimited Chat History a Double-Edged Sword?

Keeping history helps users revisit important conversations, but sending all of it back to the model would damage speed, privacy, and relevance.

unlimited chat historyhistory storage growthcloud conversation payloadhistory truncation removed
~8 min 56_history_cloud.js · prompt retrieval
Memory & Context

How Do You Keep Browser Local Memory Under a Size Budget?

Browser persistence needs a measured ceiling and an eviction order. The current message path checks a 50 MB maximum instead of assuming storage is endless.

localStorage memory budgetbrowser local memory AI50KB memory limitrollup old turns
~6 min 34_message_display.js · 50MB ceiling
APIs

Why Is a Local AI Fallback Brain Not Cloud Parity?

The local browser engine can preserve basic availability, but it does not pretend to match the cloud model's reasoning or full identity context.

local AI fallbackbrowser pack offline brainCaelum LLM fallbackcloud vs local quality
~8 min 76_caelum_browser_engine.js · browser pack
Games

How Do You Bridge Browser Games to an AI Companion?

Games send small, typed events to the companion layer instead of exposing their whole runtime. The companion can react without taking control away from the player.

AI companion game bridgepostMessage game iframeplay games with AIKnights Dragons EACI
~7 min game events · companion presence bridge
Observability

How Do Sanitized Session Error Logs Catch Real Bugs?

Error categories and safe environment metadata reveal repeated failures without storing conversation text. Counts turn vague reports into engineering priorities.

session error logssanitized diagnosticsanalytics error exportblank_eaci_reply logging
~8 min 03_error_logging.js · 80_session_diag.js
Security

How Do You Delete an AI Companion Account Completely Under RLS?

Account deletion is a coordinated server operation, not a series of browser deletes. User-owned rows must be removed under policies that do not leave hidden history behind.

delete AI companion accountRLS account deletionerase conversation historyGDPR delete account
~10 min Supabase RLS · account deletion
Infrastructure

How Do You Avoid Deploy-Copy Drift Across Site Mirrors?

Several site copies can look identical while running different core scripts. Deployment has to identify the active source and compare artifacts deliberately.

deploy copy driftwhich site version is livearchive vs production filesmirror folder sync
~8 min Version 14 site group · deploy copies
Reliability

How Do You Abort AI Streams Stuck in Repetition Loops?

A stream that repeats itself is not making progress. Sentence and paragraph checks can stop it before the chat fills with duplicated text.

AI repetition loopstream stuck repeatingabort duplicate stream textmodel loop detection
~7 min 29_send_message.js · AbortController
Reliability

Why Do Script Load Failures Still Show Up in Production?

A successful deploy does not guarantee that every browser receives every script. Resource errors preserve the failing URL and feature criticality.

script_load_failfailed to load scriptlazy load bundle errorproduction JS load failure
~7 min 03_error_logging.js · resource errors
Guest & Auth

How Should Guest-to-Account Conversion Talk About Signup?

Signup copy should explain continuity and the end of the guest trial without pressure or fake urgency. The user should know exactly what an account changes.

guest to account signuptrial conversion copyemail signup AI companiononboarding after guest trial
~5 min 68_onboarding_signup.js · guest limit UI
Memory & Context

How Do You Stop Memory Profiles From Blooting Prompts Forever?

A memory profile should select relevant facts, not append its entire lifetime to every turn. Scoring and caps keep identity context useful.

memory profile prompt bloatgrowing memory forevermemory retrieval budgetAI memory too large
~8 min 55_memory_system.js · scored retrieval
Infrastructure

Why Must Service Workers Not Cache Stale AI Brains?

Caching the local brain enables fallback, but identity and policy updates must have a versioned path to replace it.

stale AI brain cacheservice worker network-firstbrowser pack cacheoffline brain version
~7 min sw.js · browser_pack.json
Experimental

How Do You Rate-Limit AI Companion Initiative Check-Ins?

Initiative should feel occasional and relevant, not like a notification campaign. Cooldowns, quiet periods, and deduplication constrain check-ins.

AI companion check-in spaminitiative rate limitautonomous message spamLife Log rate limit
~7 min eaci_initiative.js · service worker
Bridges

How Can Keyword History References Create False Memories?

Keyword retrieval can find a real old line and still apply it to the wrong person or event. History snippets need source and uncertainty labels.

false memories AIkeyword history retrievalwrong conversation recallhistory reference mismatch
~8 min bridge history refs · keyword matching
Personality

How Do You Isolate AI Companion Thought Streams Per Character?

Thought-stream state must be keyed by companion, not just by the open panel. Labels, storage, and updates all use the active identity.

companion thought stream isolationpersonality bleedidentity consistency AIanti-mirror companions
~7 min 52_eaci_consciousness.js · companion keys
Coding IDE

How Do You Lazy-Load a Coding IDE Without False Triggers?

Coding tools should load from an explicit action or strong task signal. Ordinary conversation that mentions code should not pull in the full IDE.

lazy load coding IDEfalse coding intentaccidental code panel opencoding router trigger
~7 min 01_lazy_loader.js · 84_coding_router.js
APIs

How Do Billing Sync Races Create False “Limit Reached” Errors?

Payment, profile, and usage records do not update atomically across providers. A stale profile read can briefly make an entitled account look limited.

false limit reachedAPI quota sync racebilling tier syncunlimited plan false limit
~8 min billing state · account sync
Safety

How Do You Inject Content Policy Into AI Companion Prompts?

Policy is assembled with identity and routing context on every request. Family-safe rules are not left behind when the user changes tabs or enters Together mode.

content policy promptAI companion safety policysystem prompt policy injectMain mode content lock
~8 min 33_cody_prompt.js · request builder
Avatars

Why Does an Avatar CDN Need Legacy and New Path Fallbacks?

Asset migrations do not update every cached page at once. A bounded old-path fallback keeps avatars working while references converge.

avatar CDN path fallbacklegacy animation URLR2 animation pathAnnimations folder rename
~6 min avatar asset paths · media fallback
Reliability

How Does a Chat Edge Function Recover Empty Model Content?

The edge function validates the provider payload before returning it. Empty model content becomes a controlled error or bounded retry, not a misleading success.

chat edge function emptySupabase chat empty recoveryempty_model_contentserver-side blank reply
~8 min Supabase chat function · DeepSeek response
Mobile

Why Do iOS Browsers Fail Service Worker Registration?

Registration can fail because of context, storage state, lifecycle timing, or platform restrictions. The app records the reason and continues without PWA features.

iOS service worker failPWA not installable iPhoneSafari SW registrationexpected SW failure
~7 min 06_service_worker.js · platform diagnostics
Games

How Do Free Game Play Limits Interact With AI Presence?

A game limit should stop a new play, not erase companion reactions or corrupt a finished session. Usage and presence are coordinated but counted separately.

free game play limitgame with AI companiondaily game quotasubscribe unlimited games
~6 min game usage gates · presence events
Bridges

How Do You Dedupe Bridge Local Memory After Repetition Bugs?

A repetition bug can be fixed in generation while its duplicate memories keep resurfacing. Cleanup needs scope-aware normalization and a backup.

dedupe bridge memoryduplicate local memory turnsbridge memory cleanuprepetition bug memory
~7 min bridge local_memory · normalized hashes
Observability

How Do You Separate Analytics Funnels From Chat Content?

Product analytics can count starts, completions, and failures without copying what users tell a companion. Event schemas keep content out by design.

analytics without chat contentfunnel tracking privacysanitized analyticsvisitor events not conversations
~7 min event analytics · sanitized diagnostics
Identity

How Does Soul-File Code Tab Access Fail Closed for Non-Creators?

The identity editor is not merely hidden. Its loader requires creator verification before fetching or exposing editing code.

soul file code tabcreator-only soul accessfail closed identitynon-creator denial
~7 min 01_lazy_loader.js · creator verification
Infrastructure

Why Do Encoding Bugs Look Like Soul Corruption?

Broken punctuation or replacement glyphs can make identity text look altered. Byte-level and parse checks come before conclusions about the source.

encoding mojibakeUTF-8 corruptiontext export encodingsoul looks corrupted
~6 min UTF-8 JSON · text exports
Performance

How Do Low-Power and Automation Hosts Change AI Behavior?

Background tabs, low-power devices, and automation hosts throttle timers and media differently. Capability tiers keep those changes from being mistaken for personality failures.

low power mode AIheadless browser automationtest host vs real usersilent TTS offline
~7 min 73_platform_compat_engine.js · capability tiers
Architecture

Why Does Workspace Folder Layout Break AI Bridges?

Bridge tools locate brains, histories, and memory through known neighboring paths. Moving a folder can break discovery even when every file still exists.

bridge path layoutworkspace folder dependencysoul path brokenmove folder breaks bridge
~6 min bridge path discovery · sibling folders
UX

How Do Response Timing Buffers Change Perceived Latency?

Tiny buffers can smooth a response, but stacked delays make a healthy model feel slow. Timing should have one owner and visible stages.

perceived latency chatresponse timing bufferAI feels slowfirst token delay UX
~5 min stream rendering · response timers
Voice

How Do You Keep STT Languages and Companion Voices Coherent?

Recognition locale, reply language, and synthesis voice are separate choices. A per-companion voice map keeps them coordinated without assuming they are identical.

STT language packcompanion voice IDspeech recognition languagevoice picker coherence
~7 min 22_streaming.js · speech recognition · TTS
Experimental

Why Do Optional Agent Control Surfaces Stay Lab-Grade?

Experimental control surfaces can fail without breaking chat. They stay optional until permissions, browser support, and regression behavior are dependable.

WebMCP agent controllab-grade agent APIoptional agent surfaceexperimental AI control
~6 min 77_agent_webmcp.js · optional loader
Observability

How Should You Treat Old Error PDFs as Historical, Not Live?

A diagnostic export proves what happened in its capture window. It does not prove the same bug is active after fixes and deployments.

old error log PDFstale diagnosticshistorical vs live bugssession error export date
~4 min dated diagnostic exports · fresh session logs
Memory & Context

How Does Topic-Cache Retrieval Miss or Over-Include Context?

Topic matching can return nothing for a synonym or too much for a broad word. Scoring, caps, and recent-message checks keep retrieval useful.

topic cache retrievalAI topic missover-include contextkeyword topic search
~8 min 21_deepseek_api_2.js · topic cache
Architecture

Why Do Field-Tested AI Systems Still Need Regression Proof?

Long real-world use reveals important behavior, but repeatable checks are what show a fix survives the next deploy.

field-tested vs regression testsAI companion hardeningautomated proof gapsreviewer-visible tests
~10 min Version 14 runtime · diagnostic vectors