Document Eve plugin architecture
This commit is contained in:
parent
8e78d7dce7
commit
5fd0107923
1 changed files with 442 additions and 0 deletions
442
docs/plugin-architecture.md
Normal file
442
docs/plugin-architecture.md
Normal file
|
|
@ -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.
|
||||
Loading…
Reference in a new issue