Validate Eve getting-started contracts

This commit is contained in:
Meta Vens 2026-08-17 23:18:15 +02:00
commit 54e4eacd13
7 changed files with 143 additions and 32 deletions

View file

@ -0,0 +1,32 @@
# Eve Two-Consumer Counter
This is the smallest executable package-consumer checkpoint for CultMesh plus
Eve. It intentionally has one owner for each decision:
- the provider node owns the counter document and command transaction;
- Eve owns the validated surface and command-receipt contracts;
- CultMesh owns typed observation and the operation boundary;
- the DOM lowerer owns HTML only;
- the headless observer owns no state.
Run it from the CultLib repository:
```powershell
powershell -ExecutionPolicy Bypass -File .\scripts\verify-eve-two-runtime-sample.ps1 `
-EveRoot ..\Eve
```
The verifier builds package artifacts, installs them into a new empty temporary
consumer, and runs `sample.mjs`. Success proves:
1. no source-tree import is required by the consumer;
2. the provider surface and receipt pass Eve's generated runtime validators;
3. a DOM click executes one idempotent provider operation;
4. the DOM binding and a separate headless observer converge on the same count;
5. the canonical receipt and state survive reopening the `.cc` store.
This is not yet the promised networked multiplayer tutorial. The DOM lowerer is
hosted by jsdom in Node and both consumers share one local CultMesh node. A real
browser transport, Odin discovery, process separation, and a second language
runtime require distinct executable gates. Keeping that limitation visible is
part of the sample contract.

View file

@ -6,6 +6,10 @@ import { JSDOM } from "jsdom";
import { z } from "zod";
import { defineDocumentType } from "cultcache-ts";
import { CultMesh } from "cultmesh-ts";
import {
parseEveCommandReceipt,
parseEveSurfaceDocument,
} from "@gamecult/eve-contracts";
import { renderEveSurface } from "@gamecult/eve-browser-lowering";
const providerId = "sample.counter-provider";
@ -22,36 +26,38 @@ const counterDocument = defineDocumentType({
const surfaceDocument = defineDocumentType({
type: "gamecult.eve.surface",
schemaId: "gamecult.eve.surface.v1",
schema: z.any(),
schema: z.unknown().transform(value => parseEveSurfaceDocument(value)),
});
const receiptDocument = defineDocumentType({
type: "sample.counter_receipt",
schemaId: "sample.counter_receipt.v1",
schema: z.object({
receiptId: z.string(),
operationId: z.literal("sample.counter.increment"),
idempotencyKey: z.string(),
count: z.number().int(),
}),
type: "gamecult.eve.command_receipt",
schemaId: "gamecult.eve.command_receipt.v1",
schema: z.unknown().transform(value => parseEveCommandReceipt(value)),
name: "receiptId",
});
function counterSurface() {
return {
type: "surface-state",
schema: "gamecult.eve.surface.v1",
providerId,
providerKind: "sample.daemon",
title: "CultMesh two-runtime counter",
version: 1,
updatedAtUtc: "2026-08-17T00:00:00Z",
surface: {
id: surfaceId,
title: "Counter",
root: {
id: "counter.root",
kind: "column",
props: {},
children: [
{
id: "counter.value",
kind: "metric",
props: { label: "Canonical count", value: 0 },
children: [],
stateBindings: [{
targetProp: "value",
pointerId: "sample.counter.count",
@ -64,10 +70,21 @@ function counterSurface() {
id: "counter.increment",
kind: "control.button",
props: { label: "Increment", command: "sample.counter.increment" },
children: [],
},
],
},
styles: {},
},
commands: [{
schema: "gamecult.eve.command.v1",
command: "sample.counter.increment",
label: "Increment",
surfaceId,
transport: "cultmesh",
authority: "provider-daemon",
result: "gamecult.eve.command_receipt.v1",
}],
};
}
@ -97,7 +114,16 @@ async function waitFor(predicate, description) {
}
}
const statePath = join(await mkdtemp(join(tmpdir(), "cultmesh-eve-two-runtime-")), "state.ccmp");
assert.throws(
() => parseEveSurfaceDocument({ providerId, surface: { id: surfaceId } }),
/Invalid Eve surface/,
);
assert.throws(
() => parseEveCommandReceipt({ receiptId: "unowned" }),
/Invalid Eve commandReceipt/,
);
const statePath = join(await mkdtemp(join(tmpdir(), "cultmesh-eve-two-runtime-")), "state.cc");
const node = await CultMesh.startNode(statePath, {
documents: [counterDocument, surfaceDocument, receiptDocument],
});
@ -120,7 +146,16 @@ const increment = CultMesh.operation("sample.counter.increment", async (_request
}));
const receipt = {
receiptId: `receipt:${idempotencyKey}`,
operationId: "sample.counter.increment",
schema: "gamecult.eve.command_receipt.v1",
commandId: idempotencyKey,
command: "sample.counter.increment",
state: "accepted",
ownerRepo: "CultLib",
authority: "provider-daemon",
providerId,
surfaceId,
issuedAtUtc: new Date().toISOString(),
sourceVersion: next.count,
idempotencyKey,
count: next.count,
};
@ -162,7 +197,21 @@ assert.equal(duplicate.receiptId, browserReceipt.receiptId);
assert.equal(headless.current.count, 1);
headless.dispose();
renderEveSurface({ providerId, surface: { id: "sample.closed" } }, host);
renderEveSurface(parseEveSurfaceDocument({
type: "surface-state",
schema: "gamecult.eve.surface.v1",
providerId,
providerKind: "sample.daemon",
title: "Closed counter",
version: 2,
updatedAtUtc: "2026-08-17T00:00:01Z",
surface: {
id: "sample.closed",
root: { id: "closed.root", kind: "column", props: {}, children: [] },
styles: {},
},
commands: [],
}), host);
dom.window.close();
await node.flush();
const reopened = await CultMesh.startNode(statePath, {
@ -178,6 +227,7 @@ console.log(JSON.stringify({
providerId,
surfaceId,
receiptId: browserReceipt.receiptId,
loweringRuntime: "jsdom",
browserCount: 1,
headlessCount: 1,
restartCount: 1,