From 5fd0107923ecfbdac66433e22a8cd9c813734d48 Mon Sep 17 00:00:00 2001 From: Meta Vens Date: Tue, 7 Jul 2026 01:10:35 +0100 Subject: [PATCH] Document Eve plugin architecture --- docs/plugin-architecture.md | 442 ++++++++++++++++++++++++++++++++++++ 1 file changed, 442 insertions(+) create mode 100644 docs/plugin-architecture.md diff --git a/docs/plugin-architecture.md b/docs/plugin-architecture.md new file mode 100644 index 0000000..3298177 --- /dev/null +++ b/docs/plugin-architecture.md @@ -0,0 +1,442 @@ +# Eve Plugin Architecture + +Eve should be a presentation kernel, not a presentation monorepo. + +The repo currently contains several renderer bodies because they were useful +proof vehicles: browser reference, iOS/UIKit, Android/Kotlin, Flutter parity, +Unity packages, and fixture lowerers. That does not mean every renderer, +domain representation, and product client should live in Eve forever. The +durable architecture is smaller and sharper: + +- Eve core owns the surface/control contract. +- Plugins own domain representation semantics. +- Renderer clients own runtime substrate. +- Providers own accepted state and side effects. +- Odin/CultMesh owns discovery and routing. + +The kernel should be boring enough that new renderers can prove conformance +without joining a single giant repo, and domain plugins can add expressive +surface kinds without forcing Eve core to absorb every product vocabulary. + +## Authority + +Eve core owns: + +- `gamecult.eve.surface.v1` and related surface schemas; +- `gamecult.eve.command.v1` and operation binding/invocation descriptors; +- CultUI authoring and lowering contracts; +- plugin manifest schema and discovery rules; +- renderer capability reporting; +- conformance and parity harness expectations; +- core component kinds that are genuinely cross-domain. + +Plugins own: + +- domain component kinds; +- domain command semantics; +- domain state projection rules; +- plugin-local fixtures and conformance cases; +- compatibility adapters for existing libraries or products. + +Renderer clients own: + +- native projection into their runtime; +- input capture and command emission; +- local sensor/media capture when relevant; +- runtime-specific capability gaps; +- screenshots, frame capture, and performance telemetry. + +Providers own: + +- live state; +- command acceptance or refusal; +- receipts; +- reconciliation; +- side effects. + +Odin/CultMesh owns: + +- provider advertisement discovery; +- plugin advertisement discovery; +- document routing; +- freshness and availability view; +- replay/subscription substrate. + +## Why Not A Monorepo + +The monorepo path is tempting because Eve already has several client bodies. +It would make local edits easy at first: add Sai, absorb Aetheria's Electron +client, add Unity full-scene lowering, add Godot, add Direct2D, keep everything +under one Makefile. That is how a kernel becomes a warehouse with a logo. + +The long-term costs are worse: + +- Unity, Electron, Godot, Swift/UIKit, Kotlin/Android, Flutter, browser, and + native Direct2D have different build systems and failure modes. +- Product semantics such as visual novels, RTS overlays, ARPG world-space HUDs, + graph maps, proof fields, and sensor fusion views change at different speeds. +- Renderer clients need to release, test, and deploy independently. +- A domain plugin should be able to evolve without asking every renderer body + to accept its source tree. +- Eve core should stay small enough that a renderer author can understand the + contract without learning every GameCult product. + +Eve can keep reference implementations and first-party packages while the +contract forms. The target is not "everything leaves Eve tomorrow." The target +is "nothing joins Eve core unless it protects a core invariant." + +## Plugin Model + +An Eve plugin extends the representation vocabulary. It does not become a +provider and does not accept commands on behalf of an app. + +Plugin responsibilities: + +- declare component kinds it introduces; +- declare command names or operation descriptors it introduces; +- declare state document schemas it can project or consume; +- provide renderer-agnostic semantics for those kinds; +- provide fallback behavior when a renderer lacks support; +- provide fixtures for the parity harness; +- optionally provide reference lowerers for web or another runtime. + +Plugins may be distributed as separate repos, package artifacts, or in-repo +packages during incubation. The ownership rule is semantic, not packaging +ceremony: the plugin owns the representation shape, while providers own live +state and renderer clients own projection. + +### Example Plugin Manifest + +```json +{ + "schema": "gamecult.eve.plugin.v1", + "pluginId": "sai.vn", + "title": "Sai Visual Novel", + "owner": "sai", + "version": "0.1.0", + "componentKinds": [ + "vn.stage", + "panel.dialogue", + "text.dialogue", + "rail.actions" + ], + "commands": [ + { + "command": "story.continue", + "schema": "sai.vn.story_command.v1", + "effect": "advance-story" + }, + { + "command": "story.choose", + "schema": "sai.vn.story_command.v1", + "effect": "choose-branch" + }, + { + "command": "story.jump", + "schema": "sai.vn.story_command.v1", + "effect": "jump-to-knot" + } + ], + "stateSchemas": [ + "sai.vn.story_session.v1", + "sai.vn.visual_manifest.v1" + ], + "fixtures": [ + { + "fixtureId": "sai.vn.basic-scene", + "schema": "gamecult.eve.surface.v1", + "path": "fixtures/basic-scene.surface.json" + } + ], + "capabilities": { + "requires": [ + "image.background", + "control.button" + ], + "optional": [ + "embed.norn", + "embed.tex", + "scene.placement.keystone" + ] + } +} +``` + +The exact fields can change, but the invariant should not: plugin metadata +must be enough for Eve, Odin, and renderer clients to know what semantics are +being introduced, what fixtures prove them, and what capability gaps must be +reported instead of hidden. + +## Discovery + +There are two different discoveries. Do not collapse them. + +Provider discovery answers: + +> What live thing owns state and accepts commands? + +Plugin discovery answers: + +> What representation vocabulary and command semantics are needed to render +> and interact with this surface correctly? + +Provider advertisements remain `gamecult.eve.provider_advertisement.v1`. +Plugin advertisements should be a separate document family, +`gamecult.eve.plugin_advertisement.v1`, or a typed plugin catalog referenced +from provider advertisements. + +Recommended discovery path: + +1. A plugin publishes a plugin advertisement through CultMesh/Odin or ships a + local package manifest. +2. A provider advertisement names the plugins its surfaces require. +3. Odin indexes providers and plugins separately. +4. A renderer asks Odin for the provider surface and the required plugin + manifests. +5. The renderer checks local plugin support. +6. Unsupported required plugin capability becomes a visible capability gap. +7. Optional unsupported capability becomes a visible degraded lowering. + +Provider advertisement sketch: + +```json +{ + "schema": "gamecult.eve.provider_advertisement.v1", + "providerId": "aetheria.rts.commander", + "surfaces": [ + { + "surfaceId": "aetheria.rts.commander.vn-briefing", + "schema": "gamecult.eve.surface.v1", + "requiresPlugins": [ + { + "pluginId": "sai.vn", + "versionRange": "^0.1.0", + "requiredCapabilities": [ + "vn.stage", + "story.choose" + ] + } + ] + } + ] +} +``` + +Plugin advertisement sketch: + +```json +{ + "schema": "gamecult.eve.plugin_advertisement.v1", + "pluginId": "sai.vn", + "ownerService": "sai", + "version": "0.1.0", + "manifestAddress": "cultmesh://asgard.sai/plugins/vn/manifest", + "schemas": [ + "sai.vn.story_session.v1", + "sai.vn.visual_manifest.v1" + ], + "componentKinds": [ + "vn.stage" + ], + "commands": [ + "story.continue", + "story.choose", + "story.jump" + ], + "fixtures": [ + "cultmesh://asgard.sai/plugins/vn/fixtures/basic-scene" + ] +} +``` + +Local development may use file manifests, but live discovery should flow through +Odin/CultMesh. A renderer must not scrape random repo paths to guess plugin +support. + +## Sai As The VN Plugin Owner + +Sai should own Eve's VN/Ink representation plugin. + +Sai's current body is already crisp: + +- static-site Ink loading through `inkjs`; +- visual manifest interpretation; +- DOM rendering for static sites; +- static-site auto-init; +- Eve surface projection through `eve.js`. + +Sai should not become a daemon merely because Eve needs VN semantics. Sai can +own the plugin because it understands Ink, visual manifests, choices, story +state, scene assets, and static-site constraints. The active app or provider +still owns live session state and receipts. + +Sai plugin responsibilities: + +- define `vn.stage` semantics; +- define story state projection shape; +- define `story.continue`, `story.choose`, and `story.jump` semantics; +- define visual manifest schema as Eve-facing representation input; +- define optional embedded scene affordances such as Norn/TeX placement when + they are used inside VN scenes; +- provide fixtures for Eve parity; +- provide a web/static-site reference implementation. + +Sai does not own: + +- Aetheria story session state; +- RTS or ARPG command acceptance; +- multiplayer synchronization; +- save/load authority; +- Unity/Electron/Godot renderer bodies; +- CultMesh provider routing. + +## Building An App With Eve And Sai + +An app that wants VN functionality composes four layers. + +1. App provider owns live state. + For Aetheria, this might be `aetheria.rts.commander` and + `aetheria.arpg.world`. Each provider owns its own current story/session + state, save/load integration, eligibility, command acceptance, and receipts. + +2. Sai plugin owns VN representation. + The provider uses Sai's plugin schema to express story state as `vn.stage`, + choices, speaker, line, scene, sprites, manifest-backed assets, and story + commands. + +3. Eve owns surface/control contract. + Eve validates that the surface and command descriptors conform to + `gamecult.eve.surface.v1`, the plugin manifest, and operation binding rules. + +4. Renderer client owns projection. + Electron, Unity UI Toolkit, Unity scene, Godot, browser, or Direct2D lower + the same surface locally and emit commands back to the provider's advertised + route. + +The app-provider receipt is the proof, not the button click. A Unity ARPG scene +and an Electron RTS shell can both emit `story.choose`; the Aetheria provider +decides whether that command is accepted in that runtime, records the receipt, +and republishes state. + +## Aetheria RTS And ARPG Example + +Aetheria may need VN functionality in two runtimes: + +- RTS layer: commander briefings, dialogue overlays, strategic decisions, + lore panels, faction negotiations, mission choices. +- ARPG layer: in-world conversations, diegetic prompts, scene dialogue, + cinematic overlays, localized interaction choices. + +Sai should not care which runtime is displaying the VN. Sai should care about +the VN representation: + +- current story path/knot; +- speaker; +- dialogue line; +- choices; +- scene/background/sprites; +- visual manifest references; +- story commands; +- optional embedded surfaces. + +Aetheria owns the runtime-specific meaning: + +- whether a choice is allowed during combat; +- whether a dialogue pauses simulation; +- whether RTS and ARPG sessions share story state; +- whether a choice writes to save state; +- whether multiplayer/authority rules allow the command; +- whether a receipt is visible to the player, operator, or both. + +Renderer clients own projection: + +- Electron RTS may lower `vn.stage` as a 2D overlay panel or mission briefing. +- Unity UI Toolkit may lower the same surface as a native 2D UI document. +- Unity full-scene renderer may place dialogue in world space or cinematic + scene space. +- Godot may lower the same plugin semantics into Godot controls or scene nodes. + +The shared command path: + +```text +renderer click + -> Eve command envelope + -> Aetheria provider command route + -> Aetheria accepts/denies story command + -> provider writes receipt + -> provider republishes surface state + -> renderer updates from provider state +``` + +No runtime gets to decide the story consequence just because it rendered the +button. + +## Renderer Client Repos + +Long-term renderer/client bodies should split out when they become real apps: + +- `EveElectron2D` or Aetheria-owned Electron client for RTS/editor/operator 2D. +- `EveUnityUIToolkit` for Unity UI Toolkit 2D lowering. +- `EveUnityScene` or Aetheria-owned Unity scene frontend for full game 2D/3D. +- `EveGodot` for Godot lowering. +- `Fensalir` for Direct2D/native runtime lowering if Fensalir remains the body. +- `EveWebReference` can stay in Eve core as the canonical reference renderer + unless it grows product-specific runtime ownership. + +While these live in the Eve repo as proofs, they should behave like clients of +the plugin ABI. They should not be allowed to reach into plugin source or +provider internals as a convenience. + +## Conformance + +Every plugin needs conformance fixtures. Every renderer that claims plugin +support needs to run them. + +For Sai VN: + +- basic `vn.stage` scene; +- choice command fixture; +- continue command fixture; +- visual manifest asset fixture; +- embedded Norn/TeX optional fixture; +- degraded renderer fixture for clients without scene placement; +- receipt round-trip fixture driven by a fake provider. + +The parity manifest should be extended so runtimes declare plugin support: + +```json +{ + "runtimeId": "aetheria.unity.uitoolkit", + "supportedPlugins": [ + { + "pluginId": "sai.vn", + "capabilities": [ + "vn.stage", + "story.choose", + "story.continue" + ] + } + ] +} +``` + +A renderer can be honest without being complete. Unsupported features should +show up as capability gaps, not disappear into a local approximation. + +## Repo Strategy + +Recommended path: + +1. Keep Eve core in `E:/Projects/Eve`. +2. Define `gamecult.eve.plugin.v1` and `gamecult.eve.plugin_advertisement.v1`. +3. Let Sai publish the first domain plugin manifest for VN/Ink. +4. Add Sai VN fixtures to Eve parity as plugin fixtures, not Eve core fixtures. +5. Make the browser reference load plugin manifests and report missing + capabilities. +6. Spin renderer clients out as they become real bodies. +7. Keep in-repo clients only as references or incubation bodies with explicit + exit criteria. + +Absorbing Sai into Eve is the fallback if the plugin boundary fails. It should +not be the first move. The cleaner machine is Eve as kernel, Sai as VN plugin, +Aetheria as provider, and renderer clients as separate bodies.