Skip to content

Latest commit

 

History

History
205 lines (161 loc) · 9.77 KB

File metadata and controls

205 lines (161 loc) · 9.77 KB

Architecture

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.

1. Enduring vision, principles, and rationale

Goal

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.

Dependency direction

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.

Feature and state ownership

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.

Persistence and archival record

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.

2. Proposed and future architecture, including alternatives

Extension growth

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.

Browser execution

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.

Shell evolution

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.

Alternatives deliberately rejected

  • 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.

3. Current implementation, status, and known gaps

Projects and browser model

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.

Persistence and cleanup

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.

Imports and discovery

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.

HTTP and feature boundaries

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.