This document separates the durable architectural direction from proposed work and from the implementation that exists today. Code and tests decide current behavior; the vision explains why the boundaries exist and what future work should preserve.
WorldSharp is a self-hosted, mobile-friendly roleplay and world application. It should remain approachable to a C# developer reading it years later. The system is architecturally separated but operationally simple: one ASP.NET Core host serves the API and Blazor WebAssembly client.
Ordinary use must not depend on a permanent Blazor Server or WebSocket connection. Mobile browsers suspend background tabs, and an unreliable persistent connection should not make navigation, editing, or review fail.
WorldSharp.Core
^
|
WorldSharp.Infrastructure WorldSharp.Extensions.Abstractions
^ ^
| |
WorldSharp (ASP.NET Core host) <-- Extensions/*
^
|
WorldSharp.Client / WorldSharp.UI
- Core owns durable domain records, invariants, and application-facing store contracts. It must not depend on ASP.NET Core, SQLite, Razor, or an extension implementation.
- Infrastructure implements Core contracts for external concerns such as SQLite.
- Extensions.Abstractions is a deliberately narrow, versionable extension boundary. It may depend on Core, never on host internals or Infrastructure.
- Extensions/ owns complete feature behavior: services, endpoints, Drivers, client routes, and tests.
- WorldSharp is the composition root. It selects implementations, configures services, maps APIs, and serves the application.
- WorldSharp.Client owns the shell and foundational management screens.
- WorldSharp.UI owns reusable presentation primitives, not routes or feature behavior.
The host may depend on lower layers; lower layers must not depend on the host. Core is a platform contract, not a dumping ground for complete modes.
Durable records and narrow cooperation contracts may live in Core. A user-facing mode owns its settings, orchestration, Drivers, endpoints, and UI. The base client may open an extension route but must not duplicate that extension's workflow.
Interfaces are the shell-level entry points into a Universe. Convo, Roleplay, Game, and Social Feed should be discovered through stable mode descriptors rather than repeatedly hard-coded into the Universe shell. A Scene is a bounded Roleplay lifecycle profile, not a second conversation workspace. Generic Interface identity must not turn mode-specific state into an untyped JSON bucket.
A conversation Interface may own a family of narrative timelines, but it remains one shell entry. Exactly one path is persisted as the timeline being continued; opening the Interface resolves to that path, while sibling paths remain inside the conversation's timeline menu. Heartbeats, presence candidate selection, and automatic Real-Time day close follow only the active continued path.
Features may expose typed Drivers for replaceable execution strategies. Drivers orchestrate low-level Providers and return proposals; feature services construct authoritative context, validate output, and persist accepted state. Generated output never acquires authority merely because it came from a Driver.
World history follows the same rule. Core owns branches, typed events, validation, projection, and chronology semantics. The World Tracker extension owns extraction and orchestration, not canonical truth.
The technical WorldBranch used to stage typed events is a world-change
review, not a narrative timeline. User-visible alternate futures belong to
conversation timelines forked from messages. A pending day/Scene wrapup prevents
changing the continued path. Once a range is completed, discarded, or found to
have no world changes, a path that omits that wrapped range is no longer an
ordinary conversation alternate: continuing it requires duplicating the Universe
and rolling the copy back. Legacy transcript freezing does not commit world state,
and applying a world-change review does not itself create a parallel Universe.
SQLite is the initial local store. Schema changes are ordered migrations owned by Infrastructure. Applied migrations are immutable; correction requires a new migration.
docs/ records durable decisions, rationale, proposals, and implementation
status. docs/archive/ preserves the LLMToy and Marinara material that motivated
WorldSharp. Archived documents are historical context, not current API
documentation, but current documents should retain the useful reasoning derived
from them.
Static project references are the appropriate installation mechanism while the
features are in-tree. Dynamic loading should be considered only after
independently installed extensions are a demonstrated requirement. At that point
package compatibility, migrations, failure isolation, permissions, and security
must be designed together rather than treating AssemblyLoadContext as the
feature.
Potential contribution categories include typed world-node and relationship vocabularies, context providers, Drivers, capability Providers, import/export formats, and client tools. Each category should be added only after a real feature proves a narrow typed contract and ownership/error model.
Long-running Driver work should eventually be represented by durable execution IDs and pollable status so it survives a backgrounded browser. Optional live updates may improve feedback, but sockets should not become the source of truth. Short operations may remain inline while obeying cancellation and ownership boundaries.
The intended shell is a persistent Universe workspace in which navigation and contextual tools do not unnecessarily destroy the active Interface. The detailed left-drawer, right-drawer, topbar, and extension-tool proposal is in UI Architecture. That proposal requires explicit shell and extension contracts; it is not permission for feature pages to reach into one another.
- a universal repository framework instead of focused stores;
- one untyped property-bag node for every domain concept;
- arbitrary Drivers with database or service-provider access;
- route handlers containing persistence and domain rules;
- requiring a live socket for ordinary browser operation;
- pretending static source inclusion is runtime plugin discovery.
The host is the composition root and serves the Blazor WebAssembly client.
Interactive pages disable prerendering where they require browser-scoped
HttpClient, then load state through HTTP. The current global frame has a compact
desktop rail and mobile bottom navigation. Universe and several Interface pages
also use a responsive UniverseWorkspaceNav.
Extension source is compiled in-tree only when project files and Program
include it; creating a folder alone does not install anything. Current registry
capabilities and limits are documented in Extensions.
SqliteSchema owns ordered integer-versioned migrations recorded in
schema_migrations. Integration tests exercise real temporary SQLite files.
Character artwork is stored in a managed host directory rather than SQLite and
is exposed through a constrained endpoint.
Conversation timeline selection is stored separately from path ancestry. Child
world_interfaces rows remain persisted for lineage, but shell queries expose
only each family's root Interface and route it to the selected path. Migration
backfill prefers the most recently active descendant in the
lineage carrying the latest non-failed wrapup, then ordinary message/path
activity, rather than resetting every family to its original root.
Universe deletion performs related database deletion in one transaction.
Creating a Universe for source import is not one transaction across every
service: SourceImportService compensates a failed import by deleting the new
Universe. If compensation also fails it reports both failures. Reusable cards,
Personas created before the Universe operation, and managed artwork have
separate ownership, so import does not promise all-or-nothing cleanup of those
global artifacts.
The Character Library scan recurses at most three levels, returns at most 200 valid previews, and accepts 1–100 selected files. Chub search returns 24 results per page with a 15-second timeout; the current catalogue request is host-side.
Saved source-location discovery is separate. Non-Marinara discovery walks non-ignored files and considers at most 500 candidates; Marinara discovery reads supported table files. It does not share the Library scan's depth limit.
Endpoint mappings live under WorldSharp/Api and extension server folders.
Transport handlers should validate input and call application/Core-facing
services; route code, not this document, is the complete route catalogue.
Mode-neutral Interface endpoints are shell-owned, while mode creation and
execution remain feature-owned.
Canonical history, summaries, replay ranges, Scene lifecycle, and tracker runs are durable today. Their status and remaining chronology gaps are in World Tracking, Time, and Scenes.
There is still no runtime package installation, dynamic extension loading or unloading, extension sandbox, independently installed migration model, generic repository framework, or general property-bag world node.