Skip to content
Closed
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
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@
"stdb:generate": "node scripts/generate-spacetime-bindings.mjs",
"stdb:verify-bindings": "node scripts/verify-spacetime-bindings.mjs",
"stdb:verify-additive-migration": "node scripts/verify-spacetime-additive-migration.mjs",
"stdb:verify-worker-migration": "node scripts/verify-castle-worker-additive-migration.mjs",
"stdb:publish:dev": "node scripts/publish-spacetime-dev.mjs",
"stdb:seed-world": "tsx scripts/hermes-admin.ts seed-world",
"stdb:expand-world-v3": "tsx scripts/hermes-admin.ts expand-world-v3",
Expand Down
57 changes: 57 additions & 0 deletions scripts/verify-castle-worker-additive-migration.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import assert from 'node:assert/strict';
import { readFile } from 'node:fs/promises';
import { dirname, resolve } from 'node:path';
import { fileURLToPath } from 'node:url';

const root = resolve(dirname(fileURLToPath(import.meta.url)), '..');
const schemaPath = resolve(root, 'spacetimedb/src/schema.ts');
const previousFixturePath = resolve(root, 'spacetimedb/migration-fixtures/additive-v11-schema/src/index.ts');
const fixturePath = resolve(root, 'spacetimedb/migration-fixtures/additive-v12-schema/src/index.ts');

function registrations(source, marker) {
const start = source.indexOf(marker);
const end = source.indexOf('\n});', start);
assert.ok(start >= 0 && end > start, `missing schema marker: ${marker}`);
return source.slice(start + marker.length, end)
.split(/[,\n]/)
.map(value => value.trim())
.filter(value => /^[A-Za-z][A-Za-z0-9]*$/.test(value));
}

function table(source, name) {
const start = source.indexOf(`const ${name} = table(`);
const end = source.indexOf('\n);', start);
assert.ok(start >= 0 && end > start, `missing table: ${name}`);
return source.slice(start, end);
}

const [schema, previousFixture, fixture] = await Promise.all([
readFile(schemaPath, 'utf8'),
readFile(previousFixturePath, 'utf8'),
readFile(fixturePath, 'utf8'),
]);
const current = registrations(schema, 'const warpkeep = schema({');
const previous = registrations(previousFixture, 'const db = schema({');
const candidate = registrations(fixture, 'const db = schema({');
assert.equal(previous.length, 47, 'v11 fixture must end at ref 46');
assert.deepEqual(current.slice(0, 47), previous, 'current schema changed before the v12 suffix');
assert.deepEqual(candidate.slice(0, 47), previous, 'v12 fixture changed the deployed prefix');
assert.deepEqual(candidate.slice(47), [
'realmWorkerSystemV1',
'castleWorkerV1',
'workerAssignmentV1',
'workerNodeOccupationV1',
'workerCommandIdempotencyV1',
'workerAssignmentScheduleV1',
]);
assert.deepEqual(current.slice(47), candidate.slice(47), 'module and fixture suffix differ');
for (const name of ['realmWorkerSystemV1', 'castleWorkerV1', 'workerNodeOccupationV1', 'workerAssignmentScheduleV1']) {
const definition = table(schema, name);
assert.match(definition, /public: true/);
assert.doesNotMatch(definition, /\bfid\b|accruedAmount|materializedAmount|balance|requestKey|auth/i);
}
for (const name of ['workerAssignmentV1', 'workerCommandIdempotencyV1']) {
assert.doesNotMatch(table(schema, name), /public: true/);
}
assert.match(fixture, /fixture_seed_generic_worker_sentinel_v12/);
console.log('generic worker additive migration proof passed: refs 0–46 preserved, refs 47–52 append-only, populated fixture present, public/private boundary checked');
20 changes: 19 additions & 1 deletion spacetimedb/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,9 @@ balance, advance a timer, or decide an expedition outcome.
| Browser/backend wire protocol | 3 |
| Player authentication contract | 2 |
| Genesis world generation | 3 |
| Append-only schema generation | 10 |
| Append-only schema generation | 12 (staged suffix) |
| Alpha 0.3.12 suffix | Water refs 37–40; Stone refs 41–45 |
| Generic worker suffix | refs 47–52; staged, not activated |

Deployed tables retain their original declaration order and shape. Later
features append new tables; they do not rename or delete existing data. The
Expand Down Expand Up @@ -55,6 +56,8 @@ Public subscriptions contain only shared-world presentation:
- activated Water layout, body/cell topology, and shared environment data;
- identity-minimized site occupations containing a site, phase, public
timeline, and origin castle;
- staged four-worker roster and generic node-lease projections; the public
rows contain no FID, cargo, accrual, balance, request, or auth data;
- public Community Marks projection only when its policy permits it.

Private tables contain admission, ownership, unclaimed-slot decisions, resource
Expand Down Expand Up @@ -94,6 +97,21 @@ Gold, Food, Wood, and Stone each have an independent expedition:
- private reservations prevent passive collection or another lifecycle from
truncating a valid Food, Wood, or Stone award.

The additive generic-worker suffix defines four stable workers per founded
castle. Any idle worker can gather Gold, Food, Wood, or Stone, and multiple
workers may gather the same resource at different nodes. Worker assignments
use the same canonical site catalogs, route authority, 60-second quantum, and
30-day cap as the legacy expeditions. The caller's private read projects exact
server-time availability without a write; scheduled expiry and explicit
dispatch/recall commands materialize complete quanta. There is no per-minute
write loop and no `collect` command for generic workers.

The suffix is intentionally staged. The module does not seed, activate,
backfill, or migrate production workers in this PR. Activation requires an
admin-reviewed singleton, an exact four-worker roster digest, and zero legacy
expedition, occupation, and schedule rows. The browser wire protocol remains 3
until a later client-capability release opts into the generic worker methods.

## Entry agreement and Marks

Entry and gameplay require the exact current Alpha Terms and Hegemony Social
Expand Down
13 changes: 13 additions & 0 deletions spacetimedb/migration-fixtures/additive-v12-schema/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"name": "warpkeep-additive-v12-schema-migration-fixture",
"private": true,
"version": "0.0.0",
"type": "module",
"license": "Apache-2.0",
"dependencies": {
"spacetimedb": "2.6.1"
},
"devDependencies": {
"typescript": "5.6.3"
}
}
Loading
Loading