Skip to content
This repository was archived by the owner on Sep 10, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 0 additions & 34 deletions .changeset/inspect-the-local-stores.md

This file was deleted.

41 changes: 0 additions & 41 deletions .changeset/one-session-snapshot.md

This file was deleted.

7 changes: 0 additions & 7 deletions .changeset/scaffold-ships-inspect.md

This file was deleted.

17 changes: 0 additions & 17 deletions .changeset/watch-the-fixtures.md

This file was deleted.

2 changes: 1 addition & 1 deletion clients/dart/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

This package is generated from the Eigen engine's OpenAPI specification and its
version tracks [`@eigeninteractive/server`](https://www.npmjs.com/package/@eigeninteractive/server)
exactly: 0.2.5 here is 0.2.5 there. It has no changes of its own.
exactly: 0.3.0 here is 0.3.0 there. It has no changes of its own.

See the engine's changelog:
<https://github.com/eigeninteractive/eigen-server/blob/main/packages/server/CHANGELOG.md>
Expand Down
2 changes: 1 addition & 1 deletion clients/dart/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ The server-authoritative game engine API. Client routes (`/api/engine/_*`) requi

This Dart package is automatically generated by the [OpenAPI Generator](https://openapi-generator.tech) project:

- API version: 0.2.5
- API version: 0.3.0
- Generator version: 7.17.0
- Build package: org.openapitools.codegen.languages.DartDioClientCodegen

Expand Down
2 changes: 1 addition & 1 deletion clients/dart/pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ description: >-
# `version:` is stamped by the generation script from @eigeninteractive/server's
# package.json. The wire contract is the server's, so the client carries the
# server's version. Do not edit it by hand.
version: 0.2.5
version: 0.3.0
homepage: https://eigeninteractive.com
repository: https://github.com/eigeninteractive/eigen-server
issue_tracker: https://github.com/eigeninteractive/eigen-server/issues
Expand Down
22 changes: 22 additions & 0 deletions packages/create-eigen-game/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,27 @@
# create-eigen-game

## 0.9.3

### Patch Changes

- [#51](https://github.com/eigeninteractive/eigen-server/pull/51) [`e140da5`](https://github.com/eigeninteractive/eigen-server/commit/e140da5ee5f916a4a763b17c9dc338cb836484b6) Thanks [@seenu-k](https://github.com/seenu-k)! - A scaffolded game now ships the local inspector wired up: `pnpm inspect games`, `pnpm inspect game <id|code>`, `pnpm inspect do`.

One line makes it work. The `inspect` script points at `eigen-inspect`, which the testkit already installs as a `devDependency`, and which reads the local `.wrangler` databases through Node's built-in `node:sqlite`, so there is nothing to install and no native build to approve.

- [#51](https://github.com/eigeninteractive/eigen-server/pull/51) [`8c7d54e`](https://github.com/eigeninteractive/eigen-server/commit/8c7d54eefd3920a4e0839064b15bc4e2a0e769bf) Thanks [@seenu-k](https://github.com/seenu-k)! - `test:watch` now reruns when you edit only a fixture. It did not before, and the failure was silent: you saved a case, nothing happened, and the suite you were watching kept reporting the last result.

The cause is that `twinFixtureTests` reads fixtures with `readFileSync` at collect time, so they are not in Vite's module graph. Vitest looks a changed path up in that graph, finds nothing, checks whether the file is itself a test file, and reruns nothing. Fixture JSON was never even watched, since Vitest only adds paths outside the graph to its watcher when a trigger glob names them.

A scaffold therefore now ships `server/vitest.config.mts` carrying one option:

```ts
forceRerunTriggers: [...configDefaults.forceRerunTriggers, "**/src/module/fixtures/**/*.json"],
```

This is what `forceRerunTriggers` is for. Vitest's own defaults use it for `package.json` and the config files, which are read outside the graph for the same reason, and it appends `setupFiles` and `snapshotSerializers` to the same list.

Two things worth keeping if you edit the file. Spread the defaults rather than replacing them, because config resolution is a shallow merge and a bare array quietly stops manifest and config edits from triggering a rerun. And leave the trigger off any `@cloudflare/vitest-pool-workers` project you add later: a match reruns the whole suite, which costs nothing for pure-Node tests and is not free for workerd, where a fixture cannot change the result anyway.

## 0.9.2

### Patch Changes
Expand Down
2 changes: 1 addition & 1 deletion packages/create-eigen-game/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "create-eigen-game",
"version": "0.9.2",
"version": "0.9.3",
"license": "MIT",
"description": "Scaffold an EigenInteractive game Worker and Flutter app without cloning the engine repositories.",
"type": "module",
Expand Down
7 changes: 7 additions & 0 deletions packages/kernel/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# @eigeninteractive/kernel

## 0.3.0

### Patch Changes

- Updated dependencies []:
- @eigeninteractive/rules@0.3.0

## 0.2.5

### Patch Changes
Expand Down
2 changes: 1 addition & 1 deletion packages/kernel/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@eigeninteractive/kernel",
"version": "0.2.5",
"version": "0.3.0",
"license": "MIT",
"description": "The pure decision core of the EigenInteractive engine: given a game, state, roster and intent it returns a commit plan or a rejection. No I/O, no platform APIs.",
"main": "./dist/index.js",
Expand Down
2 changes: 2 additions & 0 deletions packages/rules/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
# @eigeninteractive/rules

## 0.3.0

## 0.2.5

### Patch Changes
Expand Down
2 changes: 1 addition & 1 deletion packages/rules/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@eigeninteractive/rules",
"version": "0.2.5",
"version": "0.3.0",
"license": "MIT",
"description": "The game-rules contract for the EigenInteractive engine: GameRules, GameModule, the hooks, and the Envelope/Observation types. Pure types, zero engine dependencies.",
"main": "./dist/index.js",
Expand Down
48 changes: 48 additions & 0 deletions packages/server/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,53 @@
# @eigeninteractive/server

## 0.3.0

### Minor Changes

- [#51](https://github.com/eigeninteractive/eigen-server/pull/51) [`d0b28f4`](https://github.com/eigeninteractive/eigen-server/commit/d0b28f4397a3363027b469982451a87b282bbd25) Thanks [@seenu-k](https://github.com/seenu-k)! - The socket now carries one message, a complete per-seat **session snapshot**, and every accepted command answers with the same value. `roster`, `sync` and `frame` as separate message kinds are gone.

This is a breaking wire change, and it fixes a class of bug rather than an instance. The old shape asked a client to assemble one session out of four sources, each carrying a different slice of the truth under a different versioning scheme: an HTTP summary that carried status, unversioned `roster` messages that stopped at the lobby, a `sync` on mid-game open that carried only a version, and versioned `frame` messages that carried neither. Read that list for "how does a client learn the game became `active`" and the answer is that it cannot. Nothing on the live channel said so. A creator sitting in a full waiting room never saw a Start button, and every seat stayed in the waiting room after the game began, until the screen was disposed and re-entered.

```jsonc
{
"type": "session",
"seq": 7, // monotonic per game, incremented by EVERY commit
"gameId": "…", "shortCode": "ABC123", "access": "private",
"schemaVersion": 1, "config": { … },
"turnSeconds": null, "budgetSeconds": null, "incrementSeconds": null,
"rated": false, "ratingPool": null,
"minPlayers": 2, "maxPlayers": 2, "createdBy": "…",
"status": "active", // what moves
"players": [ … ],
"version": 3, // null in the lobby
"frame": { … } // THIS seat's observation; null in the lobby
}
```

It is sent on socket open at **every** status, and after every committed change, lobby or state. Being complete makes it idempotent: a client that applies the newest one it has seen is correct however many it missed, so there is nothing to reconstruct and no second channel to correlate against. It carries the immutable header as well as the moving parts because a game screen must not need a second source; the D1 summary stays what it always was, the index behind lists and discovery.

Hidden information is safe by construction rather than by vigilance. The envelope is projected per seat before it is sent, `frame` is only ever the receiving principal's own seat's view resolved against the roster at send time, and a socket holding no seat gets the envelope with `frame: null`, which is how a viewer learns the game started at all.

**`seq` is the new ordering key**, because `version` cannot order a lobby change that has none. Apply a snapshot when `seq` exceeds the one you hold; that single comparison resolves a command response racing its own socket push, a duplicate delivery, and a reconnect that missed nothing. One clause is added, and it is a property of the state machine rather than an exception: a `finished` or `aborted` snapshot applies whatever its `seq`, because those statuses are absorbing and the abort teardown drops the storage the counter lives in.

Gap recovery is unchanged and still animates. `GET /games/{id}/frames?from=&to=` still fills a version jump, and the missing span is played through **under the previous envelope**, so only the real snapshot may move `status`, `players` and `seq`. A client that missed a finish therefore animates the moves and then shows the outcome, instead of displaying a finished game while mid-game moves play.

New and changed surface:

- `GET /games/{id}/session` returns the snapshot over HTTP, for the paths that have no socket.
- `LobbyAccepted`, `Joined` and `Roster` are gone. `CommandAccepted` and `SoloStarted` are now `{ session }`.
- `RosterSnapshot` and `SyncMessage` are removed from the exported protocol types; `SessionSnapshot` replaces them. `FrameMessage` remains, as the payload inside a snapshot and the element type of the range fetch.
- `GameStub` gains `session(gameId, userId)`.
- The Durable Object's `meta` table gains `seq`, `short_code` and `outcomes`. Pre-1.0 and with no games depending on the engine, these are edited into the init migration in place rather than added as a second one, so **local `.wrangler` state predating this must be deleted**. Outcomes are retained rather than drained by the finish compaction, because they are kernel output that no transition row holds, so a cold open of a finished game could not otherwise be answered from the DO alone.

One incidental fix rides along: `frame` was typed non-nullable in the generated Dart client, because a nullable `$ref` lost its null branch on the way into the OpenAPI document. The schema now spells it as a union, so it emits `anyOf: [$ref, {type: null}]` and generates as `Frame?`. A lobby session would have crashed the old typing on arrival.

### Patch Changes

- Updated dependencies []:
- @eigeninteractive/kernel@0.3.0
- @eigeninteractive/rules@0.3.0

## 0.2.5

### Patch Changes
Expand Down
2 changes: 1 addition & 1 deletion packages/server/openapi.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"openapi": "3.1.0",
"info": {
"title": "EigenInteractive Engine API",
"version": "0.2.5",
"version": "0.3.0",
"description": "The server-authoritative game engine API. Client routes (`/api/engine/*`) require a Firebase ID token; the external-bot webhook (`/api/bot/action`) is HMAC-authenticated."
},
"tags": [
Expand Down
2 changes: 1 addition & 1 deletion packages/server/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@eigeninteractive/server",
"version": "0.2.5",
"version": "0.3.0",
"license": "MIT",
"description": "The deployable half of the EigenInteractive engine: the createEngine Worker factory, the BaseGameDO Durable Object base class, the D1 schema and appliers, auth, bots and push.",
"main": "./dist/index.js",
Expand Down
Loading
Loading