Skyrim Modding Substrate Survey (Merged)¶
Two independently commissioned research reports (referred to below as [Gemini] and [Compass]) answer the same question: survey the Skyrim modding ecosystem — AI-NPC frameworks, SKSE event hooks, AI package/schedule override mods, prior reputation/rumor mods, and NPC data sources — and recommend what Chronicle should build on, fork, or study. This document merges both into one filed reference.
Verified 2026-08-20: three factual disagreements between the two source reports (MinAI status, Mantella's latest release date, CHIM's license) were resolved directly against the GitHub API rather than re-researched — see
docs/decisions/open-questions.mdfor the verification detail. This file has been corrected in place; the SkyrimNet build-vs-study disagreement is a genuine open question, not a factual one, and remains tracked inopen-questions.md.
Findings¶
- [DISAGREEMENT / DESIGN-INPUT] Which existing framework to anchor on differs sharply between the two reports. [Gemini] recommends treating Mantella as the primary architecture to study (decoupled Python service + SKSE_HTTP, AGPL-3.0/MIT) and does not mention SkyrimNet's Papyrus/C++ extension API at all — it only credits SkyrimNet with an in-process memory-reading pattern. [Compass] instead identifies SkyrimNet (
MinLL/SkyrimNet-GamePlugin) as the single best integration target because of its documentedRegisterEvent/RegisterPackage/RegisterDecorator/DirectNarrationAPI, and downgrades Mantella and CHIM to "study the transport only." This is a first-order architectural fork for Chronicle: build-to-integrate against SkyrimNet's live API vs. build a standalone SKSE_HTTP-style bridge modeled on Mantella. See Details §1. - [RISK] SkyrimNet is the most capability-aligned integration target but is closed-source with no declared license. [Compass] is explicit: no LICENSE file, DLL distributed only via Releases, C++ core closed. [Gemini] doesn't flag this risk at all because it doesn't treat SkyrimNet as a build-on candidate. Before committing to SkyrimNet as a dependency, Chronicle needs explicit license clearance from the author (MinLL) — this blocks any "integrate, don't fork" plan.
- [BUILD-ON] Both reports agree: do not route high-frequency social-sim events through Papyrus. Papyrus script-queue lag is independently identified as the failure mode of the historical Skyrim Reputation mod ([Gemini] and [Compass] both cite it) and as an architectural anti-pattern for real-time event ingestion. Consensus: use a native SKSE C++ plugin (CommonLibSSE-NG) hooking
RE::ScriptEventSourceHolder/native Papyrus events directly, pushing JSON over HTTP or WebSocket to the external Python process, bypassing the Papyrus VM for anything above low-frequency events. - [BUILD-ON] Both reports converge on the same event set and transport building blocks as fallback/standalone options: powerofthree's Papyrus Extender (open source, ~442 functions / 82 events) as the event-expansion layer, and Leidtier's
SKSE_HTTP(license unconfirmed per [Compass]) or a custom CommonLibSSE-NG WebSocket plugin as the transport — this is the same stack Mantella itself uses. - [DESIGN-INPUT] Both reports independently converge on a "dual-state" / cell-hydration architecture as mandatory. Skyrim only fully simulates NPCs in the loaded cell grid (~3x3–5x5 cells); off-grid NPCs are inert beyond abstract schedule interpolation. Both reports specify the same pattern: maintain the full ~1,000-NPC belief/rumor graph symbolically in Python at all times, and on
TESCellAttachDetachEvent/OnCellAttach/OnCellDetachhydrate/dehydrate state (inject AI packages, relationship overrides, prompt context) only for actors currently in the loaded grid. [Compass] adds a specific engine caveat neither report can ignore: unloaded NPCs' schedule position is only updated for up to ~1 hour after wait/sleep/fast-travel, so Chronicle must model NPC location itself off-grid rather than trust the engine, or depend on the community fix "NPC AI Process Position Fix – NG" (Nexus 69326). - [BUILD-ON] Both reports agree Mutagen/xEdit-based programmatic extraction from Skyrim.esm/Update.esm/DLC plugins (NPC_, FACT, RELA, PACK records) is the correct ground-truth data pipeline, merged with UESP lore text (CC-BY-SA) for bios. [Gemini] names Mutagen/Synthesis (C#/.NET, GPL-3.0, active 2026) specifically; [Compass] names the "Info NPC Extractor xEdit Script" (Nexus SE 159144, Pascal/xEdit, purpose-built for Mantella/CHIM/SPID pipelines) as the best starting point. Both are viable extraction routes to the same target schema — pick one, don't build a third from scratch.
- [RISK] Both reports agree vanilla relationship (RELA) data is sparse relative to Chronicle's ambitions. [Compass] gives a concrete data point: a relationship-mod author found only "586 relationships across 397 NPCs" via xEdit on vanilla data. Chronicle will need to synthesize the bulk of its ~1,000-NPC relationship graph rather than harvest it wholesale.
- [DESIGN-INPUT] Both reports agree no prior reputation/rumor mod solved propagation — this is genuinely Chronicle's gap to fill. Skyrim Reputation is a global morality scoreboard (not per-NPC, not propagating); rumor mods (Rumors of Skyrim, Rumors like in Oblivion) are static dialogue pools gated by crime-gold counters, not systems where NPCs learn/believe/forward information. Both reports independently conclude this validates Chronicle's core thesis rather than threatens it.
- [RISK] An engine update is a live near-term threat. [Compass] flags that Bethesda announced an incoming Skyrim SE/AE update (noted Aug 14, 2026) that will historically break every native SKSE plugin (SkyrimNet, SKSE_HTTP, Papyrus Extender, Skyrim Platform) until updated. [Gemini] does not mention this. Design implication: keep the Python/DB side of Chronicle decoupled enough that only the thin in-game SKSE bridge needs a rebuild after engine updates.
- [DEFER] Both reports treat CHIM/HerikaServer's PHP+PostgreSQL(+WSL2) stack and MinAI as study-only, not adoption candidates. MinAI is confirmed deprecated by its own README ("no longer maintained... use SkyrimNet instead"), last release 2.1.3 (April 2025). CHIM's vector-embedding RAG memory design and its DB-migration plugin-manifest model ([Compass]: bind Papyrus to
CHIM_CommandReceived, registermanifest.jsonwithgit_repo/SQLmigrations/) are worth studying as design references but neither report recommends running the PHP/PSQL/WSL stack. - [DEFER] Organic Factions (EtherDynamics) and static AI-schedule overhaul mods (Immersive Citizens, AI Overhaul SSE) are study-only, cautionary examples, not architectural targets — both stalled/became brittle due to Creation-Kit authoring overhead and load-order/priority conflicts with vanilla quest systems, reinforcing the case for driving AI packages at runtime from Python rather than authoring them statically.
Details¶
1. AI-NPC frameworks¶
| Framework | Repo | License | Last maintained | Architecture | [Gemini] verdict | [Compass] verdict |
|---|---|---|---|---|---|---|
| Mantella | art-from-the-machine/Mantella + Mantella-Spell | Backend AGPL-3.0; Mantella-Spell MIT; bios CC-BY-SA | v0.14, confirmed published 2026-04-21 (verified via GitHub API; [Gemini]'s "December 2025" was stale) | Decoupled Python backend (main.py, Python 3.11) + Papyrus spell mod; moved from file polling to async HTTP via Leidtier's SKSE_HTTP in the v0.12 "Road to HTTP" rework (issue #230) |
Study architecture / reuse IPC substrate. Study client-side event forwarding and HTTP wrappers; don't build the sim inside Mantella's backend (it's tuned for real-time 1:1 dialogue, STT/TTS). | Study the transport, optionally target as a second transport. Quest-tracks-conversation + HTTP-events pattern and CSV bio pipeline (skyrim_characters.csv) are reusable. AGPL means forking+serving obligates source disclosure. |
| Pantella | Pathos14489/Pantella | AGPL-3.0 | Active fork, 2025 | Fork of Mantella expanding local LLM backends (KoboldCpp, Llama 3, Gemma) and TTS routing; per [Compass], more transparent launcher, 1,000+ NPC bios | Study — reference for multi-backend LLM/TTS routing. | Noted as a fork worth knowing. |
| CHIM (HerikaServer) | abeiro/HerikaServer | MIT (MIT-LICENSE); bios CC-BY-SA |
Active, "March 2026" / 4,000+ commits, issues into late 2025, no tagged releases | SKSE native DLL (aiagent.dll/AIAgent.esp) → WSL2 Debian VM ("DwemerDistro") running Apache2+PHP+PostgreSQL with ~500MB local vector embedding model for RAG memory |
Study memory/RAG pipeline; bypass the PHP/PostgreSQL stack. | Study, don't build on — the WSL2/PHP/PSQL footprint is exactly what to avoid. But: timestamped "Events" log (chronology every NPC pulls last-N events from for prompt context), faction/location sync, and documented plugin system (bind to CHIM_CommandReceived ModEvent, register manifest.json with git_repo+SQL migrations/) are strong design references. |
| MinAI | MinLL/MinAI | No LICENSE file (all-rights-reserved) | Last release 2.1.3, April 2025 — confirmed deprecated | PHP-plugin/Papyrus middleware extending CHIM; modular context builder; "Sapience" (SPID-driven AI-perception toggling); "Dungeon Master" hotkeys for injecting narrative events | Study context builders & Sapience — adapt the modular context-serialization design for Python state rendering. | Deprecated — study only. README states "no longer maintained... use SkyrimNet instead." Modders' Guide exposed key/value state-push mod-events and faction-based action gating (minai_NoActionsFaction) — study the design, use SkyrimNet's live API instead. |
| SkyrimNet | MinLL/SkyrimNet-GamePlugin | No LICENSE file — closed-source DLL, distributed only via Releases | Active; Beta 23 hotfix as of mid-2026 build cycle (35 releases, 700+ commits) | Single in-process SKSE C++ DLL; reads game memory directly; serves React dashboard/API on localhost:8080; worker threads; 7 specialized LLM roles |
Study direct memory event piping — replicate the pattern of reading memory pointers without IPC serialization overhead; not treated as an integration target. | Build on / integrate — best-aligned, per [Compass]. Documented Papyrus API (SkyrimNetApi) + public C++ DLL API: RegisterEvent, RegisterShortLivedEvent (TTL-bounded), RegisterAction/RegisterSubCategory/RegisterTag/RegisterDecorator, RegisterPackage/UnregisterPackage/ScheduleDelayedPackageRemoval/ClearAllPackages/HasPackage (runtime AI-package injection), DirectNarration, SendCustomPromptToLLM; lifecycle ModEvents (SkyrimNet_SpeechStarted, SkyrimNet_MemoryCreated, SkyrimNet_DiaryCreated); vector-embedded memory CRUD; Inja template system with 100+ decorators. Third-party ecosystem already builds world-state-driven systems on it (SeverActions: 38+ NPC actions; IntelEngine: autonomous NPC "Story Engine" DM). Caveat: closed DLL/unstated license — confirm licensing before building a distributed product on it. |
Note on disagreement: [Gemini]'s architectural recommendation section never proposes integrating with SkyrimNet's API at all — its blueprint is "custom SKSE plugin (CommonLibSSE-NG) + MinAI-style context builder + Mutagen ingestion," treating Mantella/CHIM/SkyrimNet uniformly as study-only prior art. [Compass] stages SkyrimNet-integration as the fastest path to a working prototype (Stage 1) with a standalone-bridge fallback (Stage 2) only if SkyrimNet's license or API stability becomes a blocker. Chronicle's team should treat this as an open decision, not resolved by the survey — see the flagged disagreement in Findings.
2. SKSE event hooks and event-forwarding bridges¶
Native/hookable events (both reports; [Gemini] frames these as C++ engine dispatcher classes via CommonLibSSE-NG, [Compass] frames the same surface via Papyrus script events plus extender functions):
| Event | Source | Utility |
|---|---|---|
TESDeathEvent / OnDeath, OnDying, OnEnterBleedout |
RE::ScriptEventSourceHolder |
Grudge resolution, inheritance, grief, high-severity rumor generation |
TESContainerChangedEvent / OnItemAdded, OnItemRemoved, OnContainerChanged |
ditto | Theft, bartering, gift-giving, obligation shifts |
TESActivateEvent / OnActivate |
ditto | Physical contact, door transitions, container searching |
TESCombatEvent / OnCombatStateChanged, OnHit |
ditto | Violent-conflict evidence, hostility spikes |
TESActorLocationChangeEvent / OnLocationChange |
ditto | Spatial rumor propagation |
TESCellAttachDetachEvent / OnCellAttach, OnCellDetach, OnCellLoad |
ditto | Symbolic-vs-hydrated simulation transition |
TESEquipEvent / OnEquip, OnUnequip |
ditto | Visual/faction-armor state context |
| Crime events (crime gold, discover-dead-body, assault, theft/pickpocket via faction+crime-gold checks) | Story Manager quest-start events | [Compass] only — direct crime-detection input for grudge/rumor triggers |
Package/schedule (OnPackageStart/OnPackageChange, OnSit/OnGetUp) |
Papyrus | [Compass] only — know when NPC behavior changes |
Transport/bridge options (consensus + additions):
- Custom SKSE C++ plugin (CommonLibSSE-NG) hooking native dispatchers directly, pushing JSON via WebSocket/HTTP — both reports' top recommendation for the performant path. [Compass] names a concrete template:
SkyrimScripting/SKSE_Template_WebSockets(supports SE/AE/GOG/VR). - powerofthree's Papyrus Extender (powerof3/PapyrusExtenderSSE, Nexus 22854) — open source, "~442 functions, 82 events" per [Compass]; a near-universal dependency (CHIM requires it). Both reports treat it as the standard event-expansion layer.
- Leidtier's SKSE_HTTP (Leidtier/SKSE_HTTP) — the mechanism Mantella uses; Papyrus builds typed dicts, serializes to JSON, POSTs, receives replies via
SKSE_HTTP_OnHttpReplyReceived/OnHttpErrorReceivedModEvents. [Compass] flags license as unconfirmed — verify before depending on/redistributing. - Skyrim Platform (skymp project, skyrim-multiplayer/skymp, GPL-3.0) — [Compass] only. Full JS/TS runtime in-process, rich
on(event)system, engine-function hooks (sendAnimationEvent,sendPapyrusEvent). Heavier dependency; GPL forces source disclosure of forks. - Kris's Papyrus Extender (Nexus 115164) and PAPER (DennisSoemers/PAPER) — [Compass] only, additional batch item events (
OnBatchItemsAdded/Removed) and impact events.
Piping high-frequency events (location/container changes) through Papyrus SendModEvent scripts is called out by both reports as a bottleneck to avoid — this is the same lesson drawn from Skyrim Reputation's failure (§4).
3. AI package/schedule override and cell hydration¶
- Skyrim NPCs run behavior via
TESPackagestacks (Sandbox, Travel, Patrol, UseItemAt, Eat, Sleep), evaluated by priority;Actor.EvaluatePackage()forces re-evaluation. Both reports describe using SPID (Spell Perk Item Distributor) to assign custom packages at runtime by actor ID/faction/keyword, then triggering evaluation. - [Gemini]: SKSE plugin sets a Global Variable or quest-alias-driven package, then calls
EvaluatePackage(). - [Compass]: prefers SkyrimNet's
RegisterPackage/UnregisterPackage/ScheduleDelayedPackageRemovalas "the best available runtime API" if integrating with SkyrimNet; IntelEngine (third-party, built on SkyrimNet) already uses this to make NPCs physically travel/act. Falls back to Papyrus + powerofthree helpers if standalone. - Static schedule-overhaul mods — Immersive Citizens – AI Overhaul (Nexus 173) and AI Overhaul SSE (Nexus 21654) — are [Compass]-only cautionary examples: ESP-record-based (load-time, not runtime), mutually incompatible, load-order-sensitive; Immersive Citizens is "effectively frozen and hostile to third-party patching." Cited as proof of the package-priority conflict surface Chronicle will face when pushing runtime packages against vanilla quest AI.
- Cell hydration, both reports agree: NPCs in the loaded grid (~3x3–5x5 cells) are fully hydrated
TESObjectREFRobjects with physics/pathfinding/AI updates; off-grid NPCs are abstract (position interpolated along schedule/road paths, no collision/script execution). Dual-state pattern: Python simulates the full 1,000-NPC belief/spatial graph continuously; on cell attach/detach events, hydrate/dehydrate active game-state overrides (relationship overrides, dialogue flags, AI packages). - [Compass]-only engine caveat: Skyrim only updates an unloaded NPC's schedule position for up to ~1 hour after wait/sleep/fast-travel — off-screen NPC positions can go stale relative to their schedule. Mitigation cited: NPC AI Process Position Fix – NG (Nexus 69326), or Chronicle must model this itself.
4. Prior reputation/rumor/consequence mods¶
| Mod | Repo/Link | License/status | Approach | Why it stalled |
|---|---|---|---|---|
| Skyrim Reputation | Nexus SE 22374 | Open/Nexus standard; released 2018, patches active ~2023 | Pure Papyrus quest-topic conditions modifying dialogue by regional/global "alignment"/morality score; MCM toggle; guard/NPC greetings react to a title | Global reputation math ran on Papyrus thread queues → latency; broad dialogue-condition overrides clobbered unique NPC quest greetings; tracking was global, not interpersonal (an NPC in Markarth reacts instantly to a Riften crime, no propagation delay). Community had to ship "Skyrim Reputation – Fixed and Patched" (Nexus 42538) plus Enai-mod compatibility patches. |
| Organic Factions | Nexus SSE 10289, EtherDynamics | Open framework / proprietary extension; ~2022 | Custom C++/Enhanced AI Framework: factions accumulate wealth, recruit, capture territory independent of player level | [Gemini]-detailed: extreme Creation-Kit authoring overhead (dozens of linked refs/templates per faction) and instability from hooking vanilla quest systems (Civil War) that rely on hardcoded stage triggers/static actor locations. |
| Rumors of Skyrim / Voiced | Nexus SE 177826 | — | [Compass]-only: 50–59 new dialogue lines, optional "crime framework" gating lines by crime-gold counts | Static dialogue pool triggered by thresholds — no NPC learns/believes/forwards anything; "framework" is just a counter. |
| Rumors like in Oblivion | Nexus 21002 | — | [Compass]-only: same static-pool pattern | Same limitation as above. |
| NPCs React To Necromancy (And More) | Nexus 70428 | — | [Compass]-only: cooldown-gated reaction lines | Reaction, not persistent state. |
Both reports converge on the same lesson independent of which specific mods they emphasize: keep belief/reputation calculation entirely out of Papyrus and out of static dialogue conditions; inject context dynamically from the external Python service instead.
5. NPC data sources¶
| Source | Link | License | Notes |
|---|---|---|---|
| Creation Kit master records (.esm) | Native engine files (Skyrim.esm, Update.esm, DLCs) | Commercial/proprietary | NPC_ (base actor/voice type), FACT (faction structure/rank/crime group/inter-faction disposition), RELA (directed relationship rank, −4 Archenemy to +4 Lover on [Gemini]'s cited scale), PACK (24-hr AI schedule). Primary ground-truth source, both reports agree. |
| Mutagen / Synthesis | Mutagen-Modding/Synthesis | GPL-3.0 | [Gemini]'s preferred extraction tool — C#/.NET library reading/parsing/exporting binary record graphs; active 2026. Related: tr4wzified/ExperienceMutagenPatcher. |
| Spriggit | integrated in Mantella-Spell | Open source | [Gemini]: converts binary .esm/.esp to YAML — useful for inspection/version control, Mutagen preferred for automated extraction. |
| Info NPC Extractor xEdit Script | Nexus SE 159144 | — | [Compass]'s preferred extraction tool — Pascal/xEdit script exporting NPC_/ACHR records (name, factions, AI data, race) to CSV/INI, purpose-built for Mantella/CHIM/SPID pipelines. |
| UESP | Skyrim:People, Skyrim:Factions (1,475 faction pages per [Compass]), UESPWiki:CSList |
CC-BY-SA 3.0 | Narrative bios/lore for ~3,000 NPCs (per [Gemini]); already used by Mantella, CHIM, SkyrimNet for bio content. |
| Kaggle "People of Skyrim" | kaggle.com/datasets/muhajipra/people-of-skyrim, scraper at github.com/muhajipra/scrapeskyrimwiki |
— | [Compass]-only: scraped via BeautifulSoup4 from UESP infoboxes, 1,009 named characters. Convenient for prototyping; verify against actual load order. |
| NPCs Have Relationships | Nexus SE 140482 | — | [Compass]-only: sanity check on RELA sparsity — author reports "586 relationships across 397 NPCs... I don't really believe that." |
| The Imperial Library | — | — | [Compass]-only: hosts cleaned game-data exports for TES titles. |
Ingestion pipeline consensus: extract structural graph (NPC_/FACT/RELA/PACK) via Mutagen or xEdit script, merge with UESP (and optionally Kaggle-scraped) lore text, normalize to JSON to seed Chronicle's Python belief/relationship graph on first run. Both reports flag that vanilla RELA coverage is sparse relative to Chronicle's ~1,000-NPC ambitions — most of the relationship graph will need to be synthesized, not harvested.
Open item for an ADR: whether Chronicle integrates against SkyrimNet's API (Compass's Stage 1 recommendation, fastest path but blocked on unconfirmed license/closed DLL) or builds a standalone SKSE_HTTP/WebSocket bridge modeled on Mantella + powerofthree's Papyrus Extender (Gemini's implicit default, and Compass's Stage 2 fallback). Both reports agree the underlying event set, cell-hydration pattern, and data-ingestion pipeline are the same regardless of which transport is chosen.