Skip to content
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
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
2 changes: 1 addition & 1 deletion .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ The commands CI runs, in `.github/workflows/rust.yml`:
In `.github/workflows/module-durable.yml`, when the change reaches the Module or the schema:

- [ ] `cargo test -p lyracore-module --test <target> -- --ignored` for each durable target the change touches (CI runs them all)
- [ ] `scripts/check-gateway-bindings.py`
- [ ] `./lyracore bindings --check`

In `.github/workflows/runtime-scripts.yml`, when the change reaches `datascripts/`:

Expand Down
20 changes: 20 additions & 0 deletions .github/actions/setup-spacetime/action.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
name: Set up SpacetimeDB
description: Install the pinned CLI and standalone for offline generation and private durable tests.
runs:
using: composite
steps:
- shell: bash
env:
SPACETIME_VERSION: 2.7.1
SPACETIME_ARCHIVE_SHA256: 9b58192135733083479f30aba47dbbbc79b51db368c46cc221baa663e9c3a2c1
run: |
archive_path="$RUNNER_TEMP/spacetime.tar.gz"
cli_dir="$RUNNER_TEMP/spacetime-$SPACETIME_VERSION"
curl --fail --location --retry 3 \
"https://github.com/clockworklabs/SpacetimeDB/releases/download/v$SPACETIME_VERSION/spacetime-x86_64-unknown-linux-gnu.tar.gz" \
--output "$archive_path"
printf '%s %s\n' "$SPACETIME_ARCHIVE_SHA256" "$archive_path" | sha256sum --check --strict
mkdir -p "$cli_dir"
tar --extract --gzip --file "$archive_path" --directory "$cli_dir"
ln -sf spacetimedb-cli "$cli_dir/spacetime"
echo "$cli_dir" >> "$GITHUB_PATH"
36 changes: 4 additions & 32 deletions .github/workflows/module-durable.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,6 @@ concurrency:
permissions:
contents: read

env:
# docs/quickstart.md and install.sh pin this exactly; `lyracore preflight` enforces the match.
SPACETIME_VERSION: 2.7.1
# Digest of the x86_64 Linux archive in the upstream v2.7.1 GitHub release.
SPACETIME_ARCHIVE_SHA256: 9b58192135733083479f30aba47dbbbc79b51db368c46cc221baa663e9c3a2c1

jobs:
# Balance measured target durations, reserving time for each job's builds and Gateway checks.
durable:
Expand All @@ -36,37 +30,15 @@ jobs:
persist-credentials: false
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2

# Each test uses its own temporary CLI configuration.
- name: Install SpacetimeDB ${{ env.SPACETIME_VERSION }}
timeout-minutes: 3
run: |
archive_path="$RUNNER_TEMP/spacetime.tar.gz"
cli_dir="$RUNNER_TEMP/spacetime-$SPACETIME_VERSION"
curl --fail --location --retry 3 \
"https://github.com/clockworklabs/SpacetimeDB/releases/download/v$SPACETIME_VERSION/spacetime-x86_64-unknown-linux-gnu.tar.gz" \
--output "$archive_path"
printf '%s %s\n' "$SPACETIME_ARCHIVE_SHA256" "$archive_path" | sha256sum --check --strict
mkdir -p "$cli_dir"
tar --extract --gzip --file "$archive_path" --directory "$cli_dir"
ln -s spacetimedb-cli "$cli_dir/spacetime"
echo "$cli_dir" >> "$GITHUB_PATH"

# Verify the downloaded CLI matches the version required by the Module.
- name: Check the pinned version
run: |
found=$(spacetime --version | sed -n 's/.*version[ ]*\([0-9][0-9.]*\).*/\1/p' | head -n 1)
echo "spacetime $found"
[ "$found" = "$SPACETIME_VERSION" ]
- uses: ./.github/actions/setup-spacetime

- name: Test the CI scripts
if: matrix.shard == 0
run: python3 -m unittest discover -s scripts/tests

# This regenerates the complete private schema into a temporary directory. It does not need a
# running Realm and never publishes the Module.
- name: Check all Gateway bindings
if: matrix.shard == 0
run: scripts/check-gateway-bindings.py
- name: Prepare Gateway bindings
if: matrix.shard != 2
run: ./lyracore bindings

# Build failures report before any fixture starts. Each test process asks Cargo for the
# current artifact, caches its bytes, and publishes private copies through --bin-path.
Expand Down
4 changes: 4 additions & 0 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,8 @@ jobs:
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
- uses: ./.github/actions/setup-spacetime
- run: ./lyracore bindings
- name: Run clippy
run: cargo clippy --workspace --all-targets --all-features -- -D warnings

Expand All @@ -54,6 +56,8 @@ jobs:
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- uses: Swatinem/rust-cache@6323deb102c322ba6fcbdcafc7e3dddab59af2b6 # v2.9.2
- uses: ./.github/actions/setup-spacetime
- run: ./lyracore bindings
# Share compiled dependencies while keeping each crate's result separate.
- run: cargo test -p lyracore-importer
- run: cargo test -p lyracore-module --lib --features=debug_reducers
Expand Down
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
/target
**/*.rs.bk

# Local lifecycle state, logs, process ownership locks, and the pinned-tool caches: the `lyracore`
# Local lifecycle state, logs, generated Gateway bindings, process ownership locks, and tool caches: the `lyracore`
# CLI (.lyracore/cli/<rev>/) and the wire harness (.lyracore/wire-harness/<sha>/, #246 — a checkout
# of LyraCoreProject/wire-harness plus its own target/, which is why this must stay ignored).
/.lyracore/
Expand Down
2 changes: 1 addition & 1 deletion .lyracore-cli-rev
Original file line number Diff line number Diff line change
@@ -1 +1 @@
62807e2e2ca18d8606261b8d9272d6d232835583
ca736690e5d1d40cd967e1c7e4b428109a1a8b08
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 3 additions & 3 deletions docs/binding-schema-parity.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

`gateway/tests/schema_parity.rs` checks the ordered names and types of every table in the
Coordinator subscription list. A Module column change without matching generated bindings
breaks BSATN row decoding. Regenerate bindings according to [danger zones](danger-zones.md).
breaks BSATN row decoding. Run `./lyracore bindings` after schema changes. See [danger zones](danger-zones.md).

The test builds native Module table types with `RawModuleDefV9Builder`, then resolves nested type
references with `Typespace::inline_typerefs_in_type`. This produces the same ordered product shape
Expand All @@ -26,5 +26,5 @@ Only Coordinator subscriptions are included. The test reads `stdb/connection.rs`
completeness. Per-player subscriptions and reducer arguments are outside its scope.

The native Module dependency links on Linux, where the ELF linker removes unused Wasm host
intrinsics. The Gateway has no library target, so the test includes the self-contained generated
bindings tree through `#[path]` and compiles it into its own test binary.
intrinsics. The Gateway has no library target, so the test uses the same build-script wrapper as the Gateway to load the prepared
bindings outside the source tree and compiles them into its own test binary.
46 changes: 14 additions & 32 deletions docs/danger-zones.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,38 +20,19 @@ any line in §1 needs a human review before it ships, whoever or whatever wrote
else = publish aborts.
- New reducer ARG → change the module reducer **and** the gateway `call_reducer!` **in the same
commit** (code lockstep, NOT a database migration).
- New TABLE → **regenerate the gateway bindings**. Regeneration is the supported path here:
`spacetime generate --lang rust --out-dir gateway/src/stdb/bindings --module-path module \
--include-private --build-options='--features=debug_reducers' -y`. **`--include-private` is REQUIRED**
(without it `game_account`/`game_session`/the schedule tables/`pkg_*` silently vanish and the
gateway will not compile), and the generator snake-cases digit suffixes (`eff_p0`→`eff_p_0`,
`cond_value1`→`cond_value_1`), so a regen breaks exactly those columns whose GATEWAY CODE uses
the non-underscore name. Post-regen hand-patch (verified 2026-07-18 on the exploration-fog
regen, re-confirmed 2026-08-30 on the bot-transfer-intent regen): put `gossip_option_type.rs`
and `gossip_menu_profile_option_type.rs` `cond_value1`/`2`, and `aura_type.rs`/`mod.rs`
`eff_p0`/`1`(`_kind`), back to the no-underscore spelling. BUT LEAVE `spell_effect_type.rs`
`p_0`/`p_1` and `game_object_template_type.rs`/`debug_spawn_gameobject_reducer.rs`
`data_0`/`data_1` AS GENERATED (with the underscore) — their gateway code uses the underscore
names. The rule: match the COMMITTED (working) binding's field name, which is what the gateway
code accesses; `cargo build` E0609 "no field X" tells you which way each one goes.
Regeneration also removes the explanatory `facing` / `facing_angle` comment from
`creature_spline_type.rs`; restore it from the committed binding after every regeneration.
`scripts/check-gateway-bindings.py` runs the same private generation offline with SpacetimeDB
2.7.1, applies only these spelling and comment exceptions in its temporary output, formats it
with the pinned Rust toolchain, and compares every generated file with the committed tree.
Regeneration needs a machine that can run `spacetimedb-standalone` — schema extraction shells
out to it, so a sandbox without it cannot regenerate. Diff-review the regenerated tree before
committing (it rewrites every file; drift deletions are expected and correct), then run the
gateway tests. Do NOT hand-splice new tables into `bindings/mod.rs` — that hand-maintenance tax
is what regeneration replaces.
`gateway/tests/schema_parity.rs` structurally checks every subscribed table's binding against
the module schema — run `cargo test -p lyracore-gateway` after ANY hand edit to a
`<table>_type.rs` file to catch a missed, misordered, or mistyped column before it ships.
`game_transfer_out`, its transfer/instance reducers, `record_shard_load`, and
`record_region_load` began as narrow hand-splices when a full regeneration was unavailable or
carried too much unrelated churn. The full generated baseline now contains equivalent output
for them, and the complete-tree check covers their table and reducer definitions. The Gateway
still only needs to use a table binding when it subscribes to or reads that table.
- After a Module schema change, run `./lyracore bindings`. The CLI generates the Gateway's
private Rust bindings from the local Module with `debug_reducers`, using the pinned
SpacetimeDB and Rust versions. It needs `spacetimedb-standalone` for offline schema extraction,
but does not contact a Realm or publish anything.
- Bindings live under `.lyracore/generated/gateway/` and are never committed or hand-edited.
Use the generator's Rust field names, including digit separators such as `eff_p_0` and
`cond_value_1`. These Rust names do not rename the Module's SQL columns.
- Run `cargo test -p lyracore-gateway` after preparation. `gateway/tests/schema_parity.rs`
compares every Coordinator subscription with the Module schema. Update its field manifest
when a subscribed table changes, including explicit mappings for generated Rust names.
- Direct Gateway Cargo builds check the prepared output through the installed, pinned CLI.
Missing output, changed inputs, or edited generated files stop the build with preparation
instructions. `./lyracore bindings --check` performs the same read-only check.

**A defaulted column's default can be a VALID VALUE, not a sentinel — plan the backfill (#456).**
`cell` was END-appended to the four AOI-scoped tables as `#[default(0i64)]`, and 0 is the legitimate
Expand Down Expand Up @@ -397,6 +378,7 @@ cd <repo-root>

# Build + test BEFORE deploying: the unit commands CI runs (docs/testing.md), every time. This list
# once omitted lyracore-importer, and two broken tests sat unnoticed through about ten "all green" PRs.
./lyracore bindings
cargo test -p lyracore-importer
cargo test -p lyracore-module --lib --features=debug_reducers
cargo test -p lyracore-gateway
Expand Down
2 changes: 1 addition & 1 deletion docs/quickstart.md
Original file line number Diff line number Diff line change
Expand Up @@ -202,7 +202,7 @@ What that actually did:

1. Started SpacetimeDB on `127.0.0.1:3000` — **or reused one already listening there.** A node this
CLI did not start is never recorded and never stopped by `dev down`.
2. Built the gateway (`cargo build -p lyracore-gateway`).
2. Prepared the Gateway bindings, then built the Gateway with `cargo build -p lyracore-gateway`.
3. Published the **four fixture databases** — `lyracore`, `lyracore-kalimdor`,
`lyracore-instances` and `lyracore-realm` — always through `./lyracore publish`. Nothing in this path runs a bare
`spacetime publish`, and the command **refuses** to forward the destructive `-c` clear-publish.
Expand Down
8 changes: 3 additions & 5 deletions docs/recipes/add-a-spell-effect.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,12 +75,10 @@ Only when the effect outlives the cast. Add the table in the owning family's mod
## 7. Regenerate the Gateway bindings

Required for every new table or reducer, including a private table the Gateway never reads, because
the binding check compares the complete generated tree.
preparation covers the complete private schema.

- Run the `spacetime generate` command in [`danger-zones.md`](../danger-zones.md) section 1.2, then
restore the hand-patched names and the comment it lists.
- Run `scripts/check-gateway-bindings.py`. It must report no difference.
- Review the diff under `gateway/src/stdb/bindings/`. Distract added
- Run `./lyracore bindings`, then `cargo test -p lyracore-gateway`.
- Inspect the generated files under `.lyracore/generated/gateway/`. Distract added
`creature_distraction_type.rs`, `game_creature_distraction_table.rs` and lines in `mod.rs`.
- If the table drives a Relay, declare it in `gateway/src/codec/<family>/subscriptions.rs`. The
declaration generates its query and row callbacks. A table used only by Durable Reads goes in
Expand Down
4 changes: 1 addition & 3 deletions docs/recipes/add-an-opcode.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,7 @@ instead of a scan.

Required for every new table and every new reducer.

- Run the `spacetime generate` command in [`danger-zones.md`](../danger-zones.md) section 1.2 and
restore the hand-patched names and the comment it lists.
- Run `scripts/check-gateway-bindings.py`. It must report no difference.
- Run `./lyracore bindings`, then `cargo test -p lyracore-gateway`.
- For a Relay table, add its declaration beside its codec as described in step 11. The declaration
generates the subscription query. For a table used only by Durable Reads, add its query to
`coordinator_queries` in `gateway/src/stdb/connection.rs`.
Expand Down
8 changes: 4 additions & 4 deletions docs/schema.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,10 +41,10 @@ Start at [`architecture.md`](./architecture.md) for how these tables fit into th
limitation, not a repo convention. Put new string data in a **new table** instead — the same
one-row-plus-child-rows shape the codebase already uses (`game_creature_waypoint`,
`game_npc_text_slot`).
3. **A new table requires regenerating the gateway bindings**, and the generator snake-cases digit
suffixes, so a handful of columns must be hand-patched back. The exact patch list is in
`danger-zones.md` §1.2. `gateway/tests/schema_parity.rs` structurally checks every subscribed
table's binding against the module schema.
3. After a schema change, run `./lyracore bindings`. The generated Rust files stay outside the
source tree. Use the generator's field names, including digit separators. See `danger-zones.md`
§1.2. `gateway/tests/schema_parity.rs` checks every Coordinator subscription against the Module
schema.

And the one that is not about a single table: **a schema change must reach every shard.** A partial
publish presents as an unrelated mid-session hang, not a loud "no such table".
Expand Down
34 changes: 27 additions & 7 deletions docs/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,9 @@ what a real client shows.
checkout.
- On Linux, a C toolchain, `pkg-config` and the OpenSSL headers (`build-essential pkg-config
libssl-dev` on Ubuntu). macOS needs only the Xcode Command Line Tools.
- For the durable tier: the SpacetimeDB CLI 2.7.1 on `PATH` as `spacetime`, or named by
`SPACETIME_BIN`. [`quickstart.md`](./quickstart.md) gives the install.
- For Gateway binding preparation and the durable tier: the SpacetimeDB CLI 2.7.1 on `PATH`
as `spacetime`, with the matching standalone. [`quickstart.md`](./quickstart.md) gives the
install. Durable test fixtures also accept `SPACETIME_BIN` to locate the CLI.
- For the Runtime Script check: Bun 1.3.7.

None of the tiers needs your own client or a world database. Tests that do need one are ignored by
Expand All @@ -24,6 +25,7 @@ From `.github/workflows/rust.yml`. No SpacetimeDB node runs.

```bash
cargo fmt --all -- --check
./lyracore bindings
cargo clippy --workspace --all-targets --all-features -- -D warnings
cargo test -p lyracore-importer
cargo test -p lyracore-module --lib --features=debug_reducers
Expand Down Expand Up @@ -86,15 +88,33 @@ cargo test -p lyracore-gateway --bin lyracore-gateway stdb::subscriptions::party

## Gateway bindings

Also in `module-durable.yml`. The check regenerates the full private schema into a temporary
directory and compares it with `gateway/src/stdb/bindings`. It needs the SpacetimeDB 2.7.1 CLI and
publishes nothing.
Before Gateway tests, checks, or Clippy on a fresh checkout, prepare its bindings:

```bash
python3 -m unittest discover -s scripts/tests
scripts/check-gateway-bindings.py
./lyracore bindings
cargo test -p lyracore-gateway
```

Preparation needs the pinned SpacetimeDB CLI and standalone, builds the local Module with
`debug_reducers`, and includes private tables. It does not contact a Realm. The Rust and durable
CI workflows prepare bindings before compiling the Gateway. Schema generation and CLI publishing
use Rust flags from the checkout's Cargo configuration and ignore ambient Rust-flag overrides.

Generated files stay in `.lyracore/generated/gateway/`. The CLI reuses complete output while its
inputs and generated files match their recorded hashes. Module sources, installed Package sources,
workspace crates, Cargo manifests and lockfile, Cargo configuration, the Runtime Script event
catalogue, and toolchain pins are inputs. Source edits, Package additions and removals, and edits
through Package symlinks require preparation again.

`./lyracore bindings --check` checks freshness without running Cargo or SpacetimeDB. It prints JSON
with the generated directory and watched paths for the Gateway build script. Direct Cargo builds
use the installed CLI binary for this check, so they never install tools or start a nested build.
Prepare bindings before opening the Gateway in an editor on a fresh checkout.

A failed generation preserves the previous complete directory, but stale output cannot pass a
build. Old directories remain available to compilers already reading them. Delete
`.lyracore/generated/gateway/` to clear this cache, then prepare again.

## Runtime Scripts

From `.github/workflows/runtime-scripts.yml`:
Expand Down
6 changes: 5 additions & 1 deletion gateway/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ serde_json = "1"
syn = { version = "2", features = ["full", "visit"] }
# Work-item 220 (binding schema-parity test, gateway/tests/schema_parity.rs): the module crate
# compiles natively (its own test suite proves it — no wasm-only dep), so a dev-only dependency
# lets the test compare module `#[table]` structs against the hand-maintained gateway bindings
# lets the test compare module `#[table]` structs against the generated Gateway bindings
# structurally instead of only at live-decode time. `spacetimedb-lib` gives the same
# `SpacetimeType`/`AlgebraicType`/`RawModuleDefV9Builder` machinery the real `spacetime` CLI's
# schema extraction uses; pinned exactly so the workspace lockfile resolves it to the SAME
Expand All @@ -71,6 +71,10 @@ syn = { version = "2", features = ["full", "visit"] }
# same reason those two are.
spacetimedb-lib = "=2.7.1"

[build-dependencies]
serde = { version = "1", features = ["derive"] }
serde_json = "1"

[features]
dhat-heap = ["dep:dhat"]

Expand Down
Loading
Loading