Graduate Norn graph semantics from Eve

This commit is contained in:
Meta Vens 2026-07-10 16:10:43 +01:00
commit f9a2b6a6d6
6 changed files with 58 additions and 61 deletions

View file

@ -52,16 +52,16 @@ advertisements and typed command/receipt documents are the boundary.
- EveUnity's clean `TestProject` owns generic scene and UI Toolkit package
verification; ordinary runtime tests no longer open or mutate Aetheria.
- EveUnity publishes immutable `0.1.0` Git package tags for surface and UI
Toolkit plus scene `0.1.1`, whose generic CultMesh composition root discovers
Toolkit plus scene `0.1.4`, whose generic CultMesh composition root discovers
playable worlds without product identifiers. Its release-only consumer
resolves those tags plus CultLib's assembled Unity package and passes 26
resolves those tags plus CultLib's assembled Unity package and passes 30
Unity EditMode tests without sibling package paths. Aetheria pins the same
released sources.
- The same released-package consumer cold-starts from Aetheria's CultMesh
rendezvous endpoint with no configured provider or surface ID, discovers the
daemon's `interactive-world`, loads provider-owned assets, and reconciles
movement, targeting, and action receipts without importing Aetheria code.
- Aetheria pins the same scene `0.1.1` release; no Aetheria Unity component
- Aetheria pins the same scene `0.1.4` release; no Aetheria Unity component
constructs the generic CultMesh client transport used by that witness.
- EveUnity can start from one CultMesh rendezvous endpoint, discover a Verse,
select an advertised `interactive-world` surface, and connect without
@ -106,6 +106,9 @@ advertisements and typed command/receipt documents are the boundary.
sessions and Norn serves graph projection and measurement through CultNet
RUDP operation envelopes. A Sai surface may request
nested Norn or TeX capabilities when those plugins are available.
- Norn owns the nested graph document, layout semantics, runtime advertisement,
ABI fixture, and conformance evidence. Eve's Sai contract fixture carries only
an `embed.norn` capability request and Norn-owned CultMesh document reference.
- EvePlugins owns the TeX sidecar and KaTeX-backed semantic witness.
- EvePlugins owns the independent `fields.surface` sidecar, generic
`gamecult.fields.*` document model, fixtures, tests, and CultNet witness.
@ -114,7 +117,8 @@ advertisements and typed command/receipt documents are the boundary.
- Aetheria publishes objects, gravity, and render splats as
`gamecult.fields.*` documents and advertises `fields.surface` on graphical
surfaces that use them. Hermodr carries decoded MessagePack values without
interpreting field tuples; the browser fields adapter owns that conversion.
interpreting field schemas or tuples; each runtime's Fields adapter owns
conversion and projection.
- The browser fields adapter owns field component matching, canvas lifecycle,
provider-document polling, tuple conversion, Canvas/WebGL projection, GPU
resources, asset overlays, and capability-gap reporting. The general CultUI
@ -172,8 +176,6 @@ advertisements and typed command/receipt documents are the boundary.
- Publish the shared TypeScript and Rust CultNet operation hosts from tagged
CultLib releases. Sai, Norn, and TeX prove the same operation envelope across
both language implementations; stdio remains a local diagnostic adapter.
- Graduate remaining graph fixture ownership fully to Norn once no Eve fixture
is its semantic owner. Sai already owns VN/Ink execution and its live witness.
- Keep nested plugin availability explicit and non-transitive: Sai does not own
Norn or TeX.
- Move any remaining provider-specific field source composition only when it

View file

@ -169,27 +169,24 @@ typesetting semantics.
`embed.norn` means "run Norn here." It is not a request to draw a static graph
with whatever local widget happens to be nearby.
Minimum props:
Minimum boundary:
- `engine.id`: `norn`
- `engine.contract`: Norn surface contract id, currently
`gamecult.norn.surface.v1`.
- `engine.web.wasm`: optional WASM asset for browser clients.
- `engine.native.rustCrate`: native solver crate, usually `norn-rs`.
- `layout`: layout mode and solver knobs.
- `graph.nodes` and `graph.edges`: provider graph state.
- `interaction.nodeAction`: command emitted when a node is activated.
- `placement`: scene placement instructions.
- `pluginId`: `norn.graph`.
- `capability`: `embed.norn`.
- an embedded-document slot with `schemaId: norn.graph.document.v1` and a
provider/Norn-owned CultMesh `documentId`.
- `interaction.nodeAction`: command emitted to the composing provider when a
node is activated.
- `placement`: scene placement instructions owned by the composing surface.
Each target renderer embeds Norn by using the strongest local path available:
Graph nodes, edges, solver settings, runtime packages, WASM assets, and native
crate hints belong to Norn's document and plugin advertisement. They are not
inline Eve or Sai surface semantics.
- Web: Norn WASM / `@gamecult/norn-viewer`.
- iOS: native graph view using the Norn layout output, eventually Rust FFI.
- Android: native graph view using the same Norn contract, eventually Rust FFI.
- Windows Direct2D: Norn layout lowered to Direct2D/DirectWrite primitives.
If a client cannot run Norn, it must report a capability gap. It may show a
read-only fallback, but it must not silently pretend the fallback is full Norn.
The Norn plugin advertisement declares its invocation and projection paths. A
runtime resolves the advertised `embed.norn` capability without importing Norn
internals. If it cannot do so, it must report a capability gap. It may show a
read-only fallback, but it must not silently claim full Norn support.
### `embed.tex`

View file

@ -9,10 +9,11 @@
"path": "web/fixtures/sai-vn-surface.json"
},
"purpose": "Proves a Sai-owned visual-novel plugin surface can compose independently-owned Norn and TeX nested surfaces.",
"exitCriteria": "Provider scenarios remain in Eve only as contract fixtures; Sai owns VN/Ink story state, commands, projection, and plugin ABI cases.",
"exitCriteria": "Provider scenarios remain in Eve only as contract fixtures; Sai owns VN/Ink state and commands, while Norn and TeX own their nested documents, semantics, runtime advertisements, and plugin ABI cases.",
"asserts": [
"vn.stage component",
"embed.norn component",
"Norn-owned embedded graph document reference",
"embed.tex component",
"story command descriptors",
"required Sai plugin capability",

View file

@ -57,43 +57,21 @@
{
"id": "sai.graph",
"kind": "embed.norn",
"embeddedDocuments": [
{
"slotId": "graph",
"documentId": "cultmesh://norn/graphs/sai-nested-norn",
"schemaId": "norn.graph.document.v1",
"presentationKind": "embed.norn"
}
],
"props": {
"label": "GameCult Ink Knot Graph",
"engine": {
"id": "norn",
"contract": "gamecult.norn.surface.v1",
"web": {
"wasm": "/static/interactive/norn/norn-rs.wasm",
"package": "@gamecult/norn-viewer"
},
"native": {
"rustCrate": "norn-rs",
"direct2dAdapter": "fensalir.norn.direct2d"
}
},
"layout": {
"mode": "norn.2d",
"iterations": 220,
"rankGap": 150,
"nodeGap": 88,
"edgeLength": 132
},
"graph": {
"nodes": [
{ "id": "hub", "label": "Hub", "target": "hub", "x": 0.2, "y": 0.2, "current": true },
{ "id": "eve", "label": "Eve", "target": "eve", "x": 0.72, "y": 0.24 },
{ "id": "sai", "label": "Sai", "target": "sai", "x": 0.48, "y": 0.55 }
],
"edges": [
{ "source": "hub", "target": "eve" },
{ "source": "hub", "target": "sai" },
{ "source": "sai", "target": "eve" }
]
},
"pluginId": "norn.graph",
"capability": "embed.norn",
"interaction": {
"nodeAction": "story.jump",
"focusFollowsStory": true,
"rendererMustUseNorn": true
"focusFollowsStory": true
},
"placement": {
"space": "scene",

View file

@ -34,6 +34,11 @@
"kind": "owner-repo",
"ref": "Sai",
"summary": "Sai owns VN/Ink semantics once the plugin ABI graduates; Eve only hosts this fixture while the contract is forming."
},
{
"kind": "plugin-advertisement",
"ref": "Norn/plugins/norn-graph.plugin-advertisement.json",
"summary": "Norn owns graph documents, layout semantics, runtime hints, and graph conformance fixtures."
}
],
"surfaces": [

View file

@ -6,6 +6,7 @@ import { findProviderCatalogEntry, mergeProviderAdvertisement } from "./provider
const catalog = JSON.parse(readFileSync(new URL("./local-provider-catalog.json", import.meta.url), "utf8"));
const worldSmokeAdvertisement = JSON.parse(readFileSync(new URL("./fixtures/eve-world-smoke.provider-advertisement.json", import.meta.url), "utf8"));
const saiAdvertisement = JSON.parse(readFileSync(new URL("./fixtures/sai-vn.provider-advertisement.json", import.meta.url), "utf8"));
const saiSurface = JSON.parse(readFileSync(new URL("./fixtures/sai-vn-surface.json", import.meta.url), "utf8"));
test("merges generic world fixture transport with advertised world interaction", () => {
const catalogProvider = findProviderCatalogEntry(catalog, "eve.world-smoke");
@ -43,3 +44,16 @@ test("preserves Sai required and optional nested plugin requirements from advert
assert.deepEqual(byPlugin.get("tex.math").requiredCapabilities, []);
assert.deepEqual(byPlugin.get("tex.math").optionalCapabilities, ["embed.tex"]);
});
test("composes Norn by advertised capability and owner document", () => {
const norn = saiSurface.surface.root.children.find(component => component.kind === "embed.norn");
const [graphDocument] = norn.embeddedDocuments;
assert.equal(norn.props.pluginId, "norn.graph");
assert.equal(norn.props.capability, "embed.norn");
assert.equal(graphDocument.schemaId, "norn.graph.document.v1");
assert.equal(graphDocument.documentId, "cultmesh://norn/graphs/sai-nested-norn");
assert.equal("engine" in norn.props, false);
assert.equal("layout" in norn.props, false);
assert.equal("graph" in norn.props, false);
});